If you have done a web project with Java / JEE, you have probably implemented JAAS. But sometimes, we do not understand the complete picture how something that we have used, something common like JAAS framework, works. In one of our recent projects, we needed to implement JAAS on JBoss AS7 and found it was a bit different. JBoss documentation also didn’t provide all the details. So we thought we would document what we did, so that it might come in handy for someone else trying to do the same thing.

We used JDBC Realm. For production environment, you would probably use LDAP Realm. But steps will be more or less same.

Before we continue, it will be good to have a quick understanding of JAAS: It is an API that consists of a set of Java packages designed for user authentication and authorization; and implements a Java version of the standard pluggable authentication framework. The keyword here is “pluggable” – i.e. it does a lot of standard security related work for you without making you write a lot of code.

Here are the steps for JAAS implementation on JBoss AS7.

  1. Create user and roles tables that JAAS is going to use. Note the password field is varchar(32). This is good enough to store MD5 encrypted password.
    -- Table user
    CREATE TABLE IF NOT EXISTS `jaasProject`.`user` (
    `id` INT NOT NULL AUTO_INCREMENT ,
    `username` VARCHAR(45) NOT NULL ,
    `password` VARCHAR(32) NOT NULL ,
    PRIMARY KEY (`id`) );
    -- Table user_role
    CREATE TABLE IF NOT EXISTS `jaasProject`.`user_role` (
    `id` INT NOT NULL AUTO_INCREMENT ,
    `role` VARCHAR(64) NOT NULL ,
    `user_id` INT NOT NULL ,
    PRIMARY KEY (`id`) ,
    INDEX `fk_user_role_user1` (`user_id` ASC) ,
    CONSTRAINT `fk_user_role_user1`
    FOREIGN KEY (`user_id` )
    REFERENCES `jaasProject`.`user` (`id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION);
    
  2. Add some data to these tables. This could be done directly through SQL script, with something like this:
    INSERT INTO user VALUES ('admin.user',MD5('admin_user_secret') ); 

    But the issue we encountered is: password encrypted on JBoss server didn’t match inserted password. Therefore we used following JUnit to insert data. This worked well, since we could encrypt using MD5 and also match encoding (BASE64 in this case) on both sides. Also note, you will need JBoss security framework jar picketbox.4.0.x.FINAL.jar to use the Util class to encrypt password. You can get it from JBoss runtime. For Maven users, this is what you will need:

    <dependency>
          <groupId>org.picketbox</groupId>
          <artifactId>picketbox</artifactId>
          <version>4.0.7.Final</version>
    </dependency>
    

    Sample JUnit to insert user data and roles.

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = { "/applicationContext-test.xml"}) 
    public class UserTest {
    	@Autowired
    	protected UserService userService;
    
    	@Rollback (false)
    	@Test
    	public void test_CreateUser() {
    		User user = new User();
    		user.setUsername("admin.user");
    		user.setPassword(Util.createPasswordHash("MD5", Util.BASE64_ENCODING, null, null, "admin_user_secret"););
    		List<UserRole> userRoles = new ArrayList<UserRole> ();
    		// admin role 
    		UserRole adminRole = new UserRole();
    		adminRole.setUser(user);
    		adminRole.setRole("admin");
    		userRoles.add(adminRole);
    		// management role
    		UserRole mgmtRole = new UserRole();
    		mgmtRole.setUser(user);
    		mgmtRole.setRole("mgmt");
    		userRoles.add(mgmtRole);
    		// create user
    		user.setUserRoles(userRoles);
    		userService.createUser(user);
    	}
    }
    
  3. Now we have the user data and user roles in database, the next step is to setup on the server side. Go to installation directory for JBoss. That is: $JBOSS_HOME/standalone/configuration or $JBOSS_HOME/domain/servers//configuration. For standalone system, open standalone.xml file in $JBOSS_HOME/standalone/configuration. We assume, you have setup a datasource on server side, for eg. dsJaasProject.

            <subsystem xmlns="urn:jboss:domain:security:1.1">
                <security-domains>
    				...
                    <security-domain name="jaasProject" cache-type="default">
                        <authentication>
                            <login-module code="Database" flag="required">
                                <module-option name="dsJndiName" value="java:jboss/datasources/dsJaasProject"/>
                                <module-option name="principalsQuery" value="select password from user where username=?"/>
                                <module-option name="rolesQuery" value="select role, 'Roles' from user_role ur inner join user u on  ur.user_id = u.id where u.username =?"/>
                                <module-option name="hashAlgorithm" value="MD5"/>
                                <module-option name="hashEncoding" value="base64"/>
                                <module-option name="unauthenticatedIdentity" value="guest"/>
                            </login-module>
                        </authentication>
                    </security-domain>
                </security-domains>
            </subsystem>
    

    Save the standalone.xml and restart JBoss server.

  4. The next step is on the application side. Need to define available roles, resource and role mapping, login page and error page.
    In web.xml:

      <!-- Roles -->
      <security-role>
    	<description>Administrators</description>
    	<role-name>admin</role-name>
      </security-role>
      <security-role>
    	<description>Management</description>
    	<role-name>mgmt</role-name>
      </security-role>
      <!-- Resource / Role Mapping  -->
      <security-constraint>
    	<display-name>Admin Area</display-name>
    	<web-resource-collection>
    		<web-resource-name>admin_resources</web-resource-name>
    		<url-pattern>/admin/*</url-pattern>
    		<http-method>GET</http-method>
    		<http-method>POST</http-method>
    	</web-resource-collection>
    	<auth-constraint>
    		<description>For admin role only</description>
    		<role-name>admin</role-name>
    	</auth-constraint>
    	<user-data-constraint>
    		<transport-guarantee>NONE</transport-guarantee>
    	</user-data-constraint>
      </security-constraint>	
      <!-- Login Prompt -->		  
      <login-config><auth-method>FORM</auth-method>
      <form-login-config>
      	<form-login-page>/login/login.xhtml</form-login-page>
      	<form-error-page>/error.xhtml</form-error-page>
      	</form-login-config>
      </login-config>
    
  5. The next step is the connection between app and server. This is done via jboss-web.xml in your application. Create new file and place it in the same folder where web.xml is located. With the below definition, a JNDI lookup will be done for jaasProject in the server (which we defined earlier in standalone.xml).

    <?xml version="1.0" encoding="UTF-8"?>
    <jboss-web>
        <security-domain>java:/jaas/jaasProject</security-domain>
    </jboss-web>
    

    With this, all resources defined in web.xml will be available only if authorized by the server (if the user has the roles that have access to the resource).
    First, if not logged in, login page will be prompted. Note the login page:

    <form method="post" action="j_security_check">
                    <h:panelGrid id="panel" columns="2" border="1">
                        <h:outputLabel value="Username" /><input type="text" name="j_username" size="25" />
                        <h:outputLabel value="Password" /><input type="password" size="15" name="j_password" />
                        ....
                    </h:panelGrid>
                </form>
    

    Note the form action (j_security_check), username name attribute (j_username) and password name attribute (j_password). This will delegate the authentication to j_security_check servlet. Once authenticated by server (as defined by jdbcRealm).
    Second, if authenticated, server will validate for authorization, whether the role has access to the resources. If yes, the user will have access to the resources. If not, an error page will be displayed.

  6. That’s it. If you get stuck or have a question, please leave us a comment and we will try to help.