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. 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 assuptions about tables you use (as it’s also using plain JDBC to retrieve users from 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 best it to keep password in 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 in web for sure).
  • User to role mapping has to be done in separate database table with (at least) 2 VARCHARS

So here is entities code I’ve finally got that worked:

User:

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 password. It won’t work with what you get by default from JPA.

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

User -> Role mapping:

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 code side. Rest is Glassfish configuration.

Glassfish configuration

First you need to declare a new JDBC security realm:

Small description for what’s in the table:

JAAS Context just LEAVE IT 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’ve put something different here.
JNDI It’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 Group table. It has to be exactly the same as User Name Column you have declared 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 to edit glassfish-application.xml (or sun-application.xml whichever you have) and add authentication realm and roles:

<?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.

Advertisements

2 Responses to J2EE JDBC based authentication with JPA Entities in Glassfish

  1. Jose says:

    Hi Marek

    I like the hashPassword method from the User Entity.
    I am a beginner and I would like to know how to use it from a SessionBean to autenticate a user.

    Thanks.

  2. Pingback: JavaPins

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: