Click here to Skip to main content
15,890,512 members
Articles / Programming Languages / Java

J2EE JDBC Based Authentication With JPA Entities in Glassfish

Rate me:
Please Sign up or sign in to vote.
5.00/5 (1 vote)
10 Aug 2011CPOL2 min read 21.1K   2   1
How to do J2EE JDBC based authentication with JPA Entities in Glassfish

One of the authentication options you get in Glassfish is to store your users/roles just in database tables. This way, you can implement your custom UIs and logic for managing users. The usual solution you’ll find over the web for that is to create them by hand with SQL and fill using JDBC. What I needed was to get some entities for the users so I just could start my app and everything is created by JPA. This is not that easy, as Glassfish has some assumptions about the tables you use (as it’s also using plain JDBC to retrieve users from the database). Here’s how I’ve managed to do it using JPA 2.0 entities.

Entities

Entities have to be declared obeying some rules:

  • User name column has to be a VARCHAR and password column has to be a VARCHAR(32). Also, it is best to keep the password in a char[] array and not in String so you have full control over when it’s being destroyed and that it’s not being kept in memory for too long (it’s not very secure in this code, but you’ll find some guide on the web for sure).
  • User to role mapping has to be done in a separate database table with (at least) two VARCHARs.

So here is the entities code I finally got.

User

Java
package com.wordpress.jdevel.meetingpoint.model.security;

import java.io.Serializable;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import com.wordpress.jdevel.meetingpoint.model.security.Role.ROLE;

/**
 *
 * @author Marek Piechut
 */
@Entity(name = "USERS")
public class User implements Serializable {

    private static final long serialVersionUID = 1L;
    @Id
    @Column(name = "USER_NAME")
    private String userName;
    @Column(name = "PASSWD", length = 32, 
            columnDefinition = "VARCHAR(32)")
    private char[] password;
    @OneToOne(fetch = FetchType.EAGER, 
      cascade = CascadeType.ALL, mappedBy = "user")
    private Role role;

    protected User() {
    }

    public User(String userName, char[] password, ROLE role) {
        this.userName = userName;
        this.password = hashPassword(password);
        this.role = new Role(role, this);
    }

    public char[] getPassword() {
        return password;
    }

    public void setPassword(char[] password) {
        this.password = hashPassword(password);
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public Role getRole() {
        return role;
    }

    public void setRole(Role role) {
        this.role = role;
        role.setUser(this);
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final User other = (User) obj;
        if ((this.userName == null) ? (other.userName != null) : 
                    !this.userName.equals(other.userName)) {
            return false;
        }
        return true;
    }

    @Override
    public int hashCode() {
        int hash = 3;
        hash = 83 * hash + (this.userName != null ? this.userName.hashCode() : 0);
        return hash;
    }

    @Override
    public String toString() {
        return "com.wordpress.jdevel.meetingpoint.model.User[name=" + 
               userName + "]";
    }

    private char[] hashPassword(char[] password) {
        char[] encoded = null;
        try {
            ByteBuffer passwdBuffer = 
              Charset.defaultCharset().encode(CharBuffer.wrap(password));
            byte[] passwdBytes = passwdBuffer.array();
            MessageDigest mdEnc = MessageDigest.getInstance("MD5");
            mdEnc.update(passwdBytes, 0, password.length);
            encoded = new BigInteger(1, mdEnc.digest()).toString(16).toCharArray();
        } catch (NoSuchAlgorithmException ex) {
            Logger.getLogger(User.class.getName()).log(Level.SEVERE, null, ex);
        }

        return encoded;
    }
}

Remember to leave VARCHAR(32) on the password. It won’t work with what you get by default from JPA.

In this code, your password is encrypted with an MD5 hash, changing it to SHA1 shouldn’t be hard (it should be enough to modify MessageDigest.getInstance("MD5");). You pass it as a char[] array with what the user entered and it’s internally stored as a hash.

User -> Role Mapping

Java
package com.wordpress.jdevel.meetingpoint.model.security;

import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;

/**
 *
 * @author Marek Piechut
 */
@Entity(name = "ROLES")
public class Role implements Serializable {

    public static enum ROLE {

        ADMINISTRATOR, USER, GUEST
    }
    private static final long serialVersionUID = 1L;
    @Id
    @Column(name = "ROLE_NAME")
    @Enumerated(EnumType.STRING)
    private ROLE role;
    @Id
    @OneToOne
    @JoinColumn(name = "USER_NAME")
    private User user;

    protected Role() {
    }

    protected Role(ROLE role, User user) {
        this.role = role;
        this.user = user;
    }

    public ROLE getRole() {
        return role;
    }

    public void setRole(ROLE role) {
        this.role = role;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Role other = (Role) obj;
        if (this.role != other.role) {
            return false;
        }
        if (this.user != other.user && (this.user == null || 
                    !this.user.equals(other.user))) {
            return false;
        }
        return true;
    }

    @Override
    public int hashCode() {
        int hash = 5;
        hash = 89 * hash + (this.role != null ? this.role.hashCode() : 0);
        hash = 89 * hash + (this.user != null ? this.user.hashCode() : 0);
        return hash;
    }

    @Override
    public String toString() {
        return "com.wordpress.jdevel.meetingpoint.model.security.Group[name=" + 
               role + "]";
    }
}

That should be all from the code side. Rest is the Glassfish configuration.

Glassfish Configuration

First, you need to declare a new JDBC security realm:

A small description for what’s in the table:

JAAS ContextJust leave it as jdbcRealm; it has to be just this text. It took me quite some time to figure out why it was not working, and it was because I’d put something different there.
JNDIIt’s a JNDI name for your application data source (from JDBC Resources).

Rest is quite self explanatory. If not, just leave it as is, it will work with the entities I’ve posted. What’s not in the table is a user column name in the Group table. It has to be exactly the same as the User Name column you declared a few fields before (it’s also in the entity).

Now save it and configure your application to use it.

Configure Application

Now what you have to do is edit glassfish-application.xml (or sun-application.xml, whichever you have) and add authentication realm and roles:

XML
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE glassfish-application PUBLIC 
   "-//GlassFish.org//DTD GlassFish Application Server 3.1 Java EE Application 6.0//EN" 
   "http://glassfish.org/dtds/glassfish-application_6_0-1.dtd">
<glassfish-application>
    <realm>MeetingPoint_JDBC</realm>
    <security-role-mapping>
        <security-role-mapping>
            <role-name>ADMINISTRATOR</role-name>
            <group-name>ADMINISTRATOR</group-name>
        </security-role-mapping>
    </security-role-mapping>
    <security-role-mapping>
        <role-name>CREATOR</role-name>
        <group-name>CREATOR</group-name>
    </security-role-mapping>
    <security-role-mapping>
        <role-name>USER</role-name>
        <group-name>USER</group-name>
    </security-role-mapping>
    <security-role-mapping>
        <role-name>GUEST</role-name>
        <group-name>GUEST</group-name>
    </security-role-mapping>
</glassfish-application>

And now, you can create some users using these entities and JPA 2.0, for example using your JSF2.0 page, and they should be able to authenticate right away.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Poland Poland
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Jihemté15-Aug-11 21:17
Jihemté15-Aug-11 21:17 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.