Capítulo 2. Introdução ao JAAS

O framework JBossSX é baseado no JAAS API. Você deve entender os elementos básicos do JAAS API antes de você poder entender os detalhes de implantação do JBossSX. As seguintes seções fornecem uma introdução ao JAAS para prepará-lo à discussão sobre a arquitetura JBossSX descrita adiante neste guia.
O JAAS 1.0 API consiste de um conjunto de pacotes Java designados para autenticação e autorização do usuário. O API implementa a versão Java do framework de Módulos de Autenticação Pugláveis - Pluggable Authentication Modules (PAM) e extende a arquitetura de controle de acesso à Plataforma Java 2 para suportar autorização baseada no usuário.
O JAAS foi liberado primeiramente como um pacote de extensão para o JDK 1.3 e vinculado com o JDK 1.5. Esta introdução baseia-se na autenticação de capacidades do JAAS para implantar o modelo de segurança J2EE baseado na função declarativa, uma vez que o framework do JBossSX apenas utiliza este procedimento.
A autenticação do JAAS é executada de modo puglável. Isto permite que os aplicativos Java mantenham-se independentes das tecnologias de autenticação e permite que o gerenciador de segurança JBossSX trabalhe em diferentes infra-estruturas de segurança. A integração com a infra-estrutura de segurança é alcançada sem a alteração da implantação do gerenciador de segurança JBossSX. Você precisa apenas alterar a configuração da autenticação que a pilha JAAS usa.

2.1. Classes Principais do JAAS

As classes principais JAAS podem ser divididas em três categorias: comum, autenticação e autorização. A seguinte lista apresenta as classes de autenticação e comum uma vez que as classes específicas costumavam a implantar a funcionalidade do JBossSX descrita neste capítulo.
Segue abaixo as classes comuns:
  • Subject (javax.security.auth.Subject)
  • Principal (java.security.Principal)
Elas são as classes de autenticação:
  • Callback (javax.security.auth.callback.Callback)
  • CallbackHandler (javax.security.auth.callback.CallbackHandler)\n\t\n
  • Configuration (javax.security.auth.login.Configuration)
  • LoginContext (javax.security.auth.login.LoginContext)
  • LoginModule (javax.security.auth.spi.LoginModule)

2.1.1. Classes do Assunto e Principal

Os aplicativos devem ser primeiramente autenticados na fonte do solicitante com o objetivo de autorizar o acesso aos recursos. O framework JAAS define o termo assunto para representar uma fonte do solicitante. A classe Subject é a classe central no JAAS. O Subject representa a informação para a entidade única, tal como uma pessoa ou serviço. Ele abrange os principais da entidade, credenciais públicos e credenciais privados. Os JAAS APIs usam a interface java.security.Principal do Java 2 existente para representar um principal, que é basicamente um nome digitado.
Um assunto é populado com as identidades ou principais durante o processo de autenticação. Um assunto pode possuir diversos principais. Por exemplo, uma pessoa pode possuir o principal de nome (John Doe), um principal de número de segurança social (123-45-6789) e um principal de nome de usuário (johnd), sendo que todos ajudam a distinguir o assunto de outros assuntos. Segue abaixo dois métodos disponíveis para restauração dos principais associados com um assunto.
public Set getPrincipals() {...}
public Set getPrincipals(Class c) {...}
O getPrincipals() retorna todos os principais contidos no assunto. O getPrincipals(Class c) retorna apenas os principais que são instâncias de classe c ou uma se suas sub-classes. Um conjunto vazio é retornado caso o assunto não possua principais de combinação.
Perceba que a interface java.security.acl.Group é uma sub-interface do java.security.Principal, portanto uma instância no conjunto dos principais pode representar o agrupamento lógico de outros principais ou grupos de principais.

2.1.2. Autenticação do Assunto

A Autenticação do Assunto requer um logon JAAS. O procedimento de logon consiste das seguintes etapas:
  1. O aplicativo instancia um LoginContext e passa o nome da configuração de logon, além de um CallbackHandler para popular os objetos Callback, conforme solicitado pelos LoginModules da configuração.
  2. O LoginContext consulta um Configuration para carregar todos os LoginModules incluídos na configuração de logon nomeada. Caso tal configuração nomeada não existir, a configuração other será usada como padrão.
  3. O aplicativo invoca o método LoginContext.login.
  4. O método de logon invoca todos os LoginModules carregados. Uma vez que cada LoginModule tenta autenticar o assunto, ele invoca o método de manuseio no CallbackHandler associado para obter informações solicitadas para o processo de autenticação. A informação requerida é passada para manuseio do método na forma de um array de objetos Callback. Caso bem sucedido, os LoginModules associam principais e credenciais relevantes com o assunto.
  5. O LoginContext retorna o status de autenticação para o aplicativo. O sucesso da operação é representado pelo retorno do método de logon. A falha é representada através de um LoginException sendo lançado através do método de logon.
  6. Caso a autenticação seja bem sucedida, o aplicativo restaura o assunto autenticado usando o método LoginContext.getSubject.
  7. Após o escopo da autenticação do assunto ser completado, todos os principais e informações relacionadas associadas com o assunto pelo método de logon podem ser removidos pela invocação do método LoginContext.logout.
A classe LoginContext fornece os métodos básicos de autenticação dos assuntos e oferece uma maneira de desenvolver um aplicativo que é independente da tecnologia de autenticação subjacente. O LoginContext consulta um Configuration para determinar os serviços de autenticação configurados para um aplicativo em particular. As classes LoginModule representam os serviços de autenticação. Portanto, você pode conectar-se a diferentes módulos de logon em um aplicativo sem alterar o próprio aplicativo. O seguinte código apresenta as etapas solicitadas por um aplicativo para autenticar o assunto.
CallbackHandler handler = new MyHandler();
LoginContext lc = new LoginContext("some-config", handler);

try {
    lc.login();
    Subject subject = lc.getSubject();
} catch(LoginException e) {
    System.out.println("authentication failed");
    e.printStackTrace();
}
                        
// Perform work as authenticated Subject
// ...

// Scope of work complete, logout to remove authentication info
try {
    lc.logout();
} catch(LoginException e) {
    System.out.println("logout failed");
    e.printStackTrace();
}
                        
// A sample MyHandler class
class MyHandler 
    implements CallbackHandler
{
    public void handle(Callback[] callbacks) throws
        IOException, UnsupportedCallbackException
    {
        for (int i = 0; i < callbacks.length; i++) {
            if (callbacks[i] instanceof NameCallback) {
                NameCallback nc = (NameCallback)callbacks[i];
                nc.setName(username);
            } else if (callbacks[i] instanceof PasswordCallback) {
                PasswordCallback pc = (PasswordCallback)callbacks[i];
                pc.setPassword(password);
            } else {
                throw new UnsupportedCallbackException(callbacks[i],
                                                       "Unrecognized Callback");
            }
        }
    }
}
Os desenvolvedores integram uma tecnologia de autenticação pela criação de uma implantação da interface LoginModule. Isto permite que um administrador conecte-se a diferentes tecnologias de autenticação em um aplicativo. Você pode formar uma cadeia de múltiplos LoginModules para permitir que mais de uma tecnologia de autenticação participe no processo de autenticação. Por exemplo, um LoginModule pode executar a autenticação baseada no nome/senha do usuário, enquanto outro pode ser a interface para dispositivos de hardware tais como leituras de cartões e autenticadores biométricos.
O ciclo de vida de um LoginModule é dirigido pelo objeto LoginContext pelo qual o cliente crie e imprime o método de logon. O processo consiste em duas fases. Segue abaixo as etapas do processo:
  • O LoginContext cria cada LoginModule configurado usando o construtor não-arg.
  • Cada LoginModule é inicializado por uma chamada para inicializar cada método. Garante-se que o argumento Subject seja não-nulo. A assinatura do método inicializar é a seguinte: public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options).
  • O método login é chamado para iniciar o processo de autenticação. Por exemplo, a implantação do método pode pedir a um usuário pelo nome e senha do usuário e verificar a informação sobre os dados armazenados no serviço de nomeação, tais como o NIS ou LDAP. Implantações alternativas podem servir como interface para cartões smart e dispositivos biométricos, ou simplesmente extrair a informação do usuário a partir do sistema de operação subjacente. A validação da identidade do usuário por cada LoginModule é considerada fase 1 da autenticação do JAAS. A assinatura do método de login é o boolean login() throws LoginException. O LoginException indica falha. O valor de retorno verdadeiro indica que o método é bem sucedido, onde o valor de retorno negativo indica que o módulo de logon deve ser ignorado.
  • Caso a autenticação de visão geral do LoginContext seja bem sucedida, o commit é invocado em cada LoginModule. Caso a fase 1 suceda para um LoginModule, o método de confirmação continua na fase 2 e associa os principais relevantes, credenciais públicos e/ou credenciais privados com o assunto. Caso a fase 1 falhar para o LoginModule, o commit removerá qualquer estado de autenticação armazenado anteriormente, tal como nomes e senhas de usuário. A assinatura do método commit é a seguinte: boolean commit() throws LoginException. A falha em completar a fase de confirmação é indicada pelo lançamento de um LoginException. O retorno como verdadeiro indica que o método é bem sucedido, onde o retorno como falso indica que o módulo de logon deve ser ignorado.
  • Caso a autenticação da visão geral do LoginContext falhar, o método abort será invocado em cada LoginModule. O método abort remove ou destrói qualquer estado de autenticação criado pelos métodos de logon ou inicialização. A assinatura do método abort é o boolean abort() throws LoginException. A falha em completar a fase abort é indicada pelo lançamento de um LoginException. O retorno como verdadeiro indica que o método foi bem sucedido, onde o retorno como falso indica que o módulo de logon deve ser ignorado.
  • Para remover o estado de autenticação após o logon com êxito, o aplicativo invoca o logout no LoginContext. Este, por sua vez resulta numa invocação de método logout em cada LoginModule. O método logout remove os principais e credenciais originalmente associados com o assunto durante a operação commit. Os credenciais devem ser destruídos na remoção. A assinatura do método logout é a seguinte: boolean logout() throws LoginException. A falha em completar o processo de saída é indicada pelo lançamento de um LoginException. O retorno como verdadeiro indica que o método foi bem sucedido, onde o retorno como falso indica que o módulo de logon deve ser ignorado.
Quando o LoginModule necessitar comunicar-se com o usuário para obtenção de informação da autenticação, ele usará um objeto CallbackHandler. Os aplicativos implementam a interface CallbackHandler e passam-as ao LoginContext, que envia a informação de autenticação diretamente aos módulos de logon subjacentes.
Os módulos de logon usam ambos CallbackHandler para obterem entradas dos usuários, tais como uma senha ou PIN de cartão smart, além de fornecerem informação aos usuários tais como informação de status. Quando permitindo que o aplicativo especifique o CallbackHandler, os LoginModules subjacentes continuam independentes das diversas maneiras em que os aplicativos interagem com os usuários. Por exemplo, uma implantação do CallbackHandler para um aplicativo GUI pode exibir uma janela para solicitar a entrada do usuário. Por outro lado, uma implantação CallbackHandler para um ambiente sem GUI, tal como o servidor do aplicativo, pode simplesmente obter informação do credencial pelo uso de um API do servidor do aplicativo. A interface callbackhandler possui um método para implantação:
void handle(Callback[] callbacks)
    throws java.io.IOException, 
           UnsupportedCallbackException;
A interface Callback é a última classe de autenticação que iremos observar. Ela é uma interface de marcação pela qual diversas implantações padrões são fornecidas, incluindo o NameCallback e PasswordCallback usado numa amostra anterior. O LoginModule usa um Callback para obter informação solicitada pelo mecanismo de autenticação. Os LoginModules passam um array dos Callbacks diretamente ao método CallbackHandler.handle durante a fase de logon da autenticação. Caso um callbackhandler não entender como usar um objeto Callback passado a um método de manuseio, ele lança um UnsupportedCallbackException para abortar a chamada de logon.