Glassfish 3.0.1, JDBCRealm, Form-based security in JSF2

The following steps describe how to use FORM based authentication with JSF2 on Glassfish 3.0.1, retrieving security data from a JDBCRealm by default provided by Glassfish.

1) First step, we create database tables containing the security information, using PostgreSQL in this case. Please replace [variables] with your own values, always omitting the [].

CREATE TABLE users (
   userid   BIGIN NOT NULL CONSTRAINT USERS_PK PRIMARY KEY,
   username VARCHAR(100) NOT NULL UNIQUE,
   password VARCHAR(100) NOT NULL);

ALTER TABLE users OWNER TO [UserEmployedInJDBCConfig];

CREATE TABLE groups (
   groupname VARCHAR(100) NOT NULL CONSTRAINT GROUPS_PK PRIMARY KEY,
   groupdesc VARCHAR(200));

ALTER TABLE groups OWNER TO [UserEmployedInJDBCConfig];

CREATE TABLE user_groups (
   userid  BIGINT NOT NULL,
   groupname VARCHAR(100) NOT NULL,
   username VARCHAR(100) NOT NULL,
   CONSTRAINT user_groups_pk PRIMARY KEY(userid, groupname),
   CONSTRAINT users_fk  FOREIGN KEY(userid) REFERENCES users(userid),
   CONSTRAINT groups_fk FOREIGN KEY(groupname) REFERENCES groups(groupname));

ALTER TABLE user_groups OWNER TO [UserEmployedInJDBCConfig];

-- Add some initialising data
INSERT INTO groups(groupname, groupdesc) VALUES ('USER' ,'Users');
INSERT INTO groups(groupname, groupdesc) VALUES ('ADMIN', 'Administrators');

-- Add 2 users: admin: a/a and user u/u
INSERT INTO users(userid,username,password) VALUES (1, 'u', '7b774effe4a349c6dd82ad4f4f21d34c');
INSERT INTO user_groups(userid, groupname, username)  VALUES (1, 'USER', 'u');
INSERT INTO users(userid,username,password) VALUES (2, 'a', '0cc175b9c0f1b6a831c399e269772661');
INSERT INTO user_groups(userid, groupname, username)  VALUES (2, 'USER', 'a');
INSERT INTO user_groups(userid, groupname, username)  VALUES (2, 'ADMIN', 'a');
COMMIT;

2) Supposing the datasource [jdbc/mydatasource] is defined in the server configuration, we declare the JDBCRealm by adding the following code to the domain.xml file. Of course, we can do the same using the management console. The real name [myownrealm] will be used later (don’t copy the []).

<auth-realm name="[myownrealm]" classname="com.sun.enterprise.security.auth.realm.jdbc.JDBCRealm">
     <property name="jaas-context" value="jdbcRealm" />
     <property name="datasource-jndi" value="[jdbc/mydatasource]" />
     <property name="user-table" value="users" />
     <property name="user-name-column" value="username" />
     <property name="password-column" value="password" />
     <property name="group-table" value="user_groups" />
     <property name="group-name-column" value="groupname" />
     <property name="encoding" value="Hex" />
</auth-realm>

3) Next, we have to configure the web application for FORM based authentication using our realm.

Let’s start with web.xml (omit the []):

<login-config>
        <auth-method>FORM</auth-method>
        <realm-name>[myownrealm]</realm-name>
        <form-login-config>
            <form-login-page>loginpage.xhtml</form-login-page>
            <form-error-page>loginpage.xhtml</form-error-page>
        </form-login-config>
    </login-config>
    <security-role>
        <description/>
        <role-name>USER</role-name>
    </security-role>
    <security-role>
        <description/>
        <role-name>ADMIN</role-name>
    </security-role>

… and then sun-web.xml:

  <security-role-mapping>
    <role-name>USER</role-name>
    <group-name>USER</group-name>
  </security-role-mapping>
  <security-role-mapping>
    <role-name>ADMIN</role-name>
    <group-name>ADMIN</group-name>
  </security-role-mapping>

4) Now, we need to add the login form into our [loginpage.xhtml] JSF2 page. I used a Primefaces button, but any other way of classic submission will do.

<h:form id="loginformid">
    User: <h:inputText value="#{loginForm.userName}" maxlength="30"/> <br/>
    Password: <h:inputText value="#{loginForm.password}" maxlength="100"/> <br/>
    <p:commandButton value="Login" action="#{loginForm.loginAction}" ajax="false"/> <br/>
</h:form>

5) … and the managed LoginForm backing bean:

:
@RequestScoped
@Named
public class LoginForm {

    private String userName;
    private String password;

    public String loginAction() {

        HttpServletRequest req=(HttpServletRequest)FacesContext.getCurrentInstance().
                                            getExternalContext().getRequest();

        try {
            // We use the "login()" method of HttpServletRequest, available since Servlets V3,
            // for programmatic login instead of the "j_security_check" method.
            req.login(userName, password);
        }
        catch(ServletException e) {
            // Handle login error
            System.out.println("login action:"+e.getMessage());
            return null;
        }

        // We can use things like: req.isUserInRole("ADMIN");

        return null; // NULL outcome = render same page
    }
:
[getters/setters]
:
}

6) Problems with security are very hard to track in Glassfish. Error messages are not very verbose in normal cases. In order to have a detailed feedback of what the security module is doing, we have to modify the log level in the Glassfish management console.

Console
– Configuration
– Logger Settings
—- Set “javax.enterprise.system.core.security” to FINEST

Now, we also see potential SQL errors occurring when the table structure is not defined correctly.

7) NB. Continue the security configuration by defining <security-constraint>, <error-page> etc.

Leave a Reply

Your email address will not be published. Required fields are marked *