Fork me on GitHub

Programming, Internet & more

JDBC Security with Glassfish 3.1 and Java EE 6

In this tutorial I would like to show you, how you can build a JDBC Security Realm working with Glassfish 3.1 and the Java EE 6 stack. At the end of the tutorial you should have a working user management backend based on a relational database.

Most applications need some kind of security. The choice that you have while building a Java EE application is to build your own security implementation or to use the container managed security. Building your own security givens you the maximum flexibility and can be useful in some cases. But doing security right is very hard.

Using the built-in Java EE container managed security can be a worthwhile alternative. It’s not that flexible but it is well tested and suitable enough for many security requirements.
The container managed security model in Java EE is relatively simple. In fact, the container only cares about users and roles (groups). A user can be assigned to several groups. Security restrictions are then based on the groups known by the container.

Requirements

To follow the tutorial you’ll need the following software:

Setting up the Database

The most important part, while building a JDBC Realm, is to have a database table structure that a JDBC realm can understand. I will use the following tables:

  • users – Stores the user information as well as user credentials
  • user_groups – Stores the relationship between users and groups

Building the entities

I will use JPA to model the database tables as entities. In that way, we can easily manage the entities by an EntityManager and don’t have to care about table creation.
The entities look like the following:

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Version;

@Entity(name="users")
public class User {
   
    @Id
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    private Long id;
   
    @Version
    private Long version;
   
    private String username;
    private String password;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Long getVersion() {
        return version;
    }

    public void setVersion(Long version) {
        this.version = version;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
   
}
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Version;

@Entity(name="user_groups")
public class Group {
   
    @Id
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    private Long id;
   
    @Version
    private Long version;
   
    private String username;
   
    private String groupname;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Long getVersion() {
        return version;
    }

    public void setVersion(Long version) {
        this.version = version;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getGroupname() {
        return groupname;
    }

    public void setGroupname(String groupname) {
        this.groupname = groupname;
    }
   
}

As you can see, the entities are just Plain Old Java Objects (POJO) with some JPA annotations. The JPA provider uses the annotations to generate the database tables on application startup. To match the JPA conventions, we have to create a file called persistence.xml in the META-INF folder.

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
  <persistence-unit name="default" transaction-type="JTA">
    <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
    <jta-data-source>jdbc/securitydb</jta-data-source>
    <properties>
      <property name="eclipselink.ddl-generation" value="create-tables"/>
    </properties>
  </persistence-unit>
</persistence>

Since, we’re working with Glassfish, the JPA provider will be automatically EclipseLink. Make sure to use the class org.eclipse.persistence.jpa.PersistenceProvider as provider value. The property eclipselink.ddl-generation should be set to create-tables, this will force EclipseLink to generate the database tables for us, if they don’t exist.

The data source (connection to database) is set to the JNDI name jdbc/securitydb. We will create this JDBC Resource later in the Glassfish admin console.

EJB – UserService

Now, that we have created the security entities it is time to create an Enterprise Java Bean (EJB) to manage the entities. I will name the EJB UserService.

The UserService is responsible for creating new users and groups as well as assigning users to groups. In our JDBC realm the user password will be stored as encrypted SHA-256 hash. Therefore, the UserService also handles the password encryption.

In our sample application the UserService is really simple and only consists of one public method to create a new user and group.

public void createUserAndGroup(String username, String password, String groupname) {
        User user = new User();
        user.setUsername(username);
        String encodedPassword = encodePassword(password);
        user.setPassword(encodedPassword);
        em.persist(user);
       
        Group group = new Group();
        group.setGroupname(groupname);
        group.setUsername(username);
        em.persist(group);
    }

Application Configuration

After creating the service, we have to prepare our application to use the container managed security.

Let’s consider we have two JSF pages. The first one is just the welcome file called index.xhtml. This page will be public visible.

The other page is called secure.xhtml and should be only visible to registered users that belong to the group users.

For this security constraint, we have to add some entries in our web.xml.

<security-constraint>
        <display-name>Constraint1</display-name>
        <web-resource-collection>
            <web-resource-name>secure-page</web-resource-name>
            <description/>
            <url-pattern>/secure.xhtml</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <description/>
            <role-name>users</role-name>
        </auth-constraint>
    </security-constraint>

Now, we need to tell the application from which source, the users and groups should be fetched.
We want to retrieve the security information from a JDBC Security Realm called myRealm. We will create the Realm later in the Glassfish admin console.

Furthermore we have to specify which authentication mode should be used to request the security information from the user. We’ll use Basic-Authentication, that will popup a little window in the users browser.

So, just add these informations to the web.xml.

<login-config>
        <auth-method>BASIC</auth-method>
        <realm-name>myRealm</realm-name>
    </login-config>
    <security-role>
        <description/>
        <role-name>users</role-name>
    </security-role>

After that, we have to provide an additional web.xml, but this time specifically for the Glassfish server. The file is called glassfish-web.xml.
This file contains a security role mapping which maps our security groups to Java EE security roles. In our case we can just use a mapping of 1:1.
It is important to provide this file, otherwise the security realm will not work correctly.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE glassfish-web-app PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Servlet 3.0//EN" "http://glassfish.org/dtds/glassfish-web-app_3_0-1.dtd">
<glassfish-web-app error-url="">
  <security-role-mapping>
    <role-name>users</role-name>
    <group-name>users</group-name>
  </security-role-mapping>
  <class-loader delegate="true"/>
  <jsp-config>
    <property name="keepgenerated" value="true">
      <description>Keep a copy of the generated servlet class' java code.</description>
    </property>
  </jsp-config>
</glassfish-web-app>

Glassfish Database Configuration

Now we can configure our Glassfish instance to use a database as JDBC realm.

First, make sure to create a database on your MySQL Server Instance. I will name my database securitydb.

mysql -u root -p -e "create database securitydb;"

Before you proceed, grab the MySQL JDBC connector and drop it in your Glassfish library directory. Don’t forget to restart your Glassfish.

{GlassfishInstallDirectory}/glassfish/lib/

Next, we have to create a JDBC Connection Pool for the MySQL Server database. To create a connection pool, open the Glassfish management console. It is mostly located at http://localhost:4848.

In the navigation panel on the left side navigate to Resources –> JDBC –> JDBC Connection Pools and click on New.

Name your connection pool, choose java.sql.Driver as Resource Type and select MySql as Database Driver Vendor. Click on Next.

Now, make sure that the checkbox Ping is checked and provide the properties URL, user, password. The URL must be in the form

jdbc:mysql://localhost:3306/yourDatabaseName

Now, we have to create a JDBC Resource. Navigate to Resources –> JDBC –> JDBC Resources and click on New.

Give your resource a JNDI name, I will use jdbc/securitydb. Make sure, to choose right Connection Pool Name, in our case SecurityPool and click on Finish.

Glassfish Realm Configuration

As soon as we’ve finished our database configurations, we can create the JDBC Security Realm.

Navigate to Configurations –> Security –> Realms and click on New.

Give the realm a name and choose com.sun.enterprise.security.auth.realm.jdbc.JDBCRealm as Class Name.
Use the following values as properties:

  • JAAS Context: jdbcRealm – It is important to use exactly this value to create a JDBC realm
  • JNDI: jdbc/securitydb – Use the previously created JDBC Resource
  • User Table: users – The tablename that holds the user accounts
  • User Name Column: username – The name of the column that holds the username
  • Password Column: password – The name of the column that holds the user password
  • Group Table: user_groups – The tablename that stores the user2groups assignment
  • Group Table User Name Column: username – The name of the column that holds the username in the user_groups table
  • Group Name Column: groupname – The name of the column that holds the groupname in the user_groups table
  • Digest Algorithm: SHA-256 – Use the password encryption algorithm that you use to encrypt the user password
  • Password Encryption Algorithm: none – This has to be set to none
  • Encoding: Base64 – The encoding algorithm of the user password

Click on OK. To be sure, that your newly created JDBC Realm is active and restart the Glassfish.

Deployment

After the configuration, we can create a War-File of our application and deploy it to the Glassfish.

I will use maven in my projects, so the building of a War-File can be simplified to the command

mvn clean compile package

The generated War-File can then be uploaded using the Glassfish admin console. Please make sure to use an easy to remember Context Root path, as this will be the path where you can access the application. I will use securityapp.

Testing

After a successful deployment, you can access the application by accessing the url http://localhost:8080/securityapp.

This should redirect you to the public visible index.xhtml page. On this page, you can create new users. Create a new user called admin with the password admin.

Let’s test the security and open the secure page at http://localhost:8080/securityapp/secure.xhtml. This should bring up the Basic-Authentication popup.

For testing, just enter a user that you don’t have created yet. You should not be able to see the secure page.

Now enter the admin user and password. You should see the secure page. Which proves that the JDBC Security Realm works correctly.

Source Code

As always, the full source is availabe on GitHub.

Category: javaee
-->

Post a Comment

Your email is kept private. Required fields are marked *

*
*