12.2.2. Amostra do LoginModule Personalizado

A seguinte informação o ajudará a criar uma amostra do Módulo de Logon que estende o UsernamePasswordLoginModule e obtém uma senha de usuário e nomes de função a partir da observação do JNDI.
No final desta seção, você terá criado um módulo de logon do contexto JNDI personalizado que retornará uma senha de usuário, caso você execute uma busca no contexto usando o nome do password/<username> do formulário (onde o <username> é o usuário atual sendo autenticado). Similarmente, uma busca do roles/<username> do formulário retorna funções de usuário requeridas.
O Exemplo 12.11, “Módulo de Logon Personalizado do JndiUserAndPass” apresenta o código de fonte para o módulo de logon personalizado do JndiUserAndPass.
Perceba que isto estende o JBoss UsernamePasswordLoginModule, tudo o que o JndiUserAndPass realiza é obter a senha e funções do usuário a partir do armazenamento JNDI. O JndiUserAndPass não interage com as operações LoginModule do JAAS.

Exemplo 12.11. Módulo de Logon Personalizado do JndiUserAndPass

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.4 $
*/
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]);
                groups[0].addMember(role);
            }
            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));
        }
    }   
}
Os detalhes do armazenamento JNDI podem ser encontrados no org.jboss.book.security.ex2.service.JndiStore MBean. Este serviço vincula um ObjectFactory que retorna um javax.naming.Context proxy ao JNDI. O proxy manuseia as operações de busca realizadas pela checagem do prefixo do nome de busca em relação ao password e roles.
Quando o nome iniciar pelo password, a senha do usuário é solicitada. Quando o nome iniciar por roles, as funções do usuário serão solicitadas. A implementação da amostra sempre retorna uma senha de theduke e um array de nomes de funções igual ao {"TheDuke", "Echo"}, independente do nome de usuário. Você pode experimentar este procedimento com outras implementações.
O código de amostra inclui um bean de sessão simples para testar o módulo de logon personalizado. Para construção, implantação e rodagem da amostra, execute o seguinte comando no diretório de amostras:
[examples]$ ant -Dchap=security -Dex=2 run-example
...
run-example2:
     [echo] Waiting for 5 seconds for deploy...
     [java] [INFO,ExClient] Login with user name=jduke, password=theduke
     [java] [INFO,ExClient] Looking up EchoBean2
     [java] [INFO,ExClient] Created Echo
     [java] [INFO,ExClient] Echo.echo('Hello') = Hello
A escolha do uso do módulo de logon personalizado para a autenticação ao lado do servidor do usuário é determinado pela configuração de logon para o domínio de segurança da amostra. O descritor EJB JAR META-INF/jboss.xml determina o domínio de segurança.
<?xml version="1.0"?>
<jboss>
   <security-domain>security-ex2</security-domain>
</jboss>
O descritor META-INF/login-config.xml do SAR define a configuração do módulo de logon.
<application-policy name = "security-ex2">
   <authentication>
      <login-module code="org.jboss.book.security.ex2.JndiUserAndPass" flag="required">
         <module-option name="userPathPrefix">/security/store/password</module-option>
         <module-option name = "rolesPathPrefix">/security/store/roles</module-option>
      </login-module>
   </authentication>
</application-policy>