JBoss.org Community Documentation A Custom LoginModule Example

In this section we will develop a custom login module example. It will extend the UsernamePasswordLoginModule and obtains a user's password and role names from a JNDI lookup. The idea is that there is a JNDI context that will return a user's password if you perform a lookup on the context using a name of the form password/<username> where <username> is the current user being authenticated. Similarly, a lookup of the form roles/<username> returns the requested user's roles.

The source code for the example is located in the src/main/org/jboss/book/security/ex2 directory of the book examples. Example 8.9, “ A JndiUserAndPass custom login module” shows the source code for the JndiUserAndPass custom login module. Note that because this extends the JBoss UsernamePasswordLoginModule, all the JndiUserAndPass does is obtain the user's password and roles from the JNDI store. The JndiUserAndPass does not concern itself with the JAAS LoginModule operations.

package org.jboss.book.security.ex2;
import java.security.acl.Group;
import java.util.Map;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.login.LoginException;

import org.jboss.security.SimpleGroup;
import org.jboss.security.SimplePrincipal;
import org.jboss.security.auth.spi.UsernamePasswordLoginModule;

 *  An example custom login module that obtains passwords and roles
 *  for a user from a JNDI lookup.
 *  @author Scott.Stark@jboss.org
 *  @version $Revision: 1.12 $
public class JndiUserAndPass 
    extends UsernamePasswordLoginModule
    /** The JNDI name to the context that handles the password/username lookup */
    private String userPathPrefix;
    /** The JNDI name to the context that handles the roles/ username lookup */
    private String rolesPathPrefix;
     * Override to obtain the userPathPrefix and rolesPathPrefix options.
    public void initialize(Subject subject, CallbackHandler callbackHandler,
                           Map sharedState, Map options)
        super.initialize(subject, callbackHandler, sharedState, options);
        userPathPrefix = (String) options.get("userPathPrefix");
        rolesPathPrefix = (String) options.get("rolesPathPrefix");
     *  Get the roles the current user belongs to by querying the
     * rolesPathPrefix + '/' + super.getUsername() JNDI location.
    protected Group[] getRoleSets() throws LoginException
        try {
            InitialContext ctx = new InitialContext();
            String rolesPath = rolesPathPrefix + '/' + super.getUsername();

            String[] roles = (String[]) ctx.lookup(rolesPath);
            Group[] groups = {new SimpleGroup("Roles")};
            log.info("Getting roles for user="+super.getUsername());
            for(int r = 0; r < roles.length; r ++) {
                SimplePrincipal role = new SimplePrincipal(roles[r]);
                log.info("Found role="+roles[r]);
            return groups;
        } catch(NamingException e) {
            log.error("Failed to obtain groups for
                        user="+super.getUsername(), e);
            throw new LoginException(e.toString(true));
     * Get the password of the current user by querying the
     * userPathPrefix + '/' + super.getUsername() JNDI location.
    protected String getUsersPassword() 
        throws LoginException
        try {
            InitialContext ctx = new InitialContext();
            String userPath = userPathPrefix + '/' + super.getUsername();
            log.info("Getting password for user="+super.getUsername());
            String passwd = (String) ctx.lookup(userPath);
            log.info("Found password="+passwd);
            return passwd;
        } catch(NamingException e) {
            log.error("Failed to obtain password for
                        user="+super.getUsername(), e);
            throw new LoginException(e.toString(true));

Example 8.9.  A JndiUserAndPass custom login module

The details of the JNDI store are found in the org.jboss.book.security.ex2.service.JndiStore MBean. This service binds an ObjectFactory that returns a javax.naming.Context proxy into JNDI. The proxy handles lookup operations done against it by checking the prefix of the lookup name against password and roles. When the name begins with password, a user's password is being requested. When the name begins with roles the user's roles are being requested. The example implementation always returns a password of theduke and an array of roles names equal to {"TheDuke", "Echo"} regardless of what the username is. You can experiment with other implementations as you wish.

The example code includes a simple session bean for testing the custom login module. To build, deploy and run the example, execute the following command in the examples directory.

[examples]$ ant -Dchap=security -Dex=2 run-example
     [echo] Waiting for 5 seconds for deploy...
     [java] [INFO,ExClient] Login with username=jduke, password=theduke
     [java] [INFO,ExClient] Looking up EchoBean2
     [java] [INFO,ExClient] Created Echo
     [java] [INFO,ExClient] Echo.echo('Hello') = Hello

The choice of using the JndiUserAndPass custom login module for the server side authentication of the user is determined by the login configuration for the example security domain. The EJB JAR META-INF/jboss.xml descriptor sets the security domain

<?xml version="1.0"?>

The SAR META-INF/login-config.xml descriptor defines the login module configuration.

<application-policy name = "security-ex2">
        <login-module code="org.jboss.book.security.ex2.JndiUserAndPass"
            <module-option name = "userPathPrefix">/security/store/password</module-option>
            <module-option name = "rolesPathPrefix">/security/store/roles</module-option>