JAAS

JAAS (Java Authentication and Authorization Service) est une API standard de java permettant de gérer des identifications et les droits associés (par rôles) au niveau du client et du serveur d'application. JAAS est intégrée à J2SE et est l'API standard utilisée par les serveurs d'application J2EE. Elle permet de séparer la gestion des droits d'accès aux composants J2EE du code métier. Dans un premier temps, JAAS authentifie, c'est-à-dire qu'il valide l'identité du client, puis gère les autorisations, c'est-à-dire qu'il valide les droits d'accès pour un client authentifié. Pour ce faire, les identités sont regroupées en rôles et les autorisations accordées par rôle.


Sécurité des EJB

Restrictions d'accès

Les autorisations d'accès aux EJB se basent sur des permissions accordées à des rôles pour des méthodes. Elles se paramètrent dans le fichier META-INF/ejb-jar.xml.

<method-permission>
  <role-name>Role1</role-name>
  <method>
    <ejb-name>MyEjbStateless</ejb-name>
    <method-name>myBusinessMethod</method-name>
  </method>
</method-permission>

JAAS en client / serveur

Dans une architecture client / serveur à base d'EJB, JAAS se met en oeuvre sous forme de modules coté client et coté serveur, ainsi que du paramétrage des 2 parties.

PrincipeJaas.gif

Principales classes

Les principales classes mises en oeuvre sont

Représente identité du demandeur

Représente le demandeur (personne ou application), il peut avoir plusieurs identités

Interface du composant d'authentification, implémentée par le fournisseur d'authentification

Méthode login(), elle exploite les modules décrites dans la configuration de JAAS

Gestionnaire de callbacks

Interface de demande d'information (login, password)

ClassesJaas.png

Mise en oeuvre avec JBoss

Les exemples ci-dessous ont été testés avec JBoss 4.0.2.

Coté client

Callback

Les informations de login et password sont transmises au module via le callback handler. Dans notre exemple, les informations sont transmises au handler lors de sa création. Elle sont ensuite transmises au module via les objets de callback appropriés : NameCallback pour le login et PasswordCallback pour le mot de passe.

package fr.sewatech.j2ee.security.client;

import java.io.IOException;
import javax.security.auth.callback.*;

public class CustomLoginHandler implements CallbackHandler {
   
   private String login;
   private char[] password;

   public CustomLoginHandler(String login, String password) {
       this.login = login;
       this.password = password.toCharArray();
   }
   
   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(login);
           } else if (callbacks[i] instanceof PasswordCallback) {
               PasswordCallback pc = (PasswordCallback) callbacks[i];
               pc.setPassword(password);
           } else {
               throw new UnsupportedCallbackException(callbacks[i],
                       "Mauvais Callback");
           }
       }
   }
}

Login

La connexion à JAAS se fait via le LoginContext. Pour que celui-ci fonctionne correctement, avec les modules sélectionnés, il faut spécifier un fichier de configuration. La meilleure façon de procéder est de passer la propriété système -Djava.security.auth.login.config==jaas.conf.

Exemple simple de fichier de congiguration JAAS :

sewatech {
  fr.sewatech.j2ee.security.client.SwClientLoginModule  required;
}
jboss {
  // jBoss LoginModule
  org.jboss.security.ClientLoginModule  required;
};

L'appel de la méthode login du LoginContext appellera les modules en fonction du nom du contexte.

   private Subject login() throws LoginException {
       CallbackHandler handler = new CustomLoginHandler(login, password);
       // Initialisation du contexte (configuration "jboss")
       LoginContext lc = new LoginContext("jboss", handler);
       // Demande d'authentification
       lc.login();
       // Lecture du sujet authentifié
       return getSubject();
   }

Logout

La déconnexion se fait par l'appel de la méthode logout() du LoginContext.

         lc.logout(); 

Coté serveur

Le serveur d'application fournit des modules prêts à l'emploi, pour vérifier l'authentification dans une base de données, dans un fichier, dans un annuaire LDAP,... Des modules externes peuvent aussi être intégrés.

Les configurations JAAS sont décrites dans le fichier conf/login-service.xml, elles sont nommées, ce qui permettra de choisir EJB par EJB à quel domaine de sécurité ont souhaite le soumettre. L'exemple ci-dessous utilise le module de validation en base de données ; il est nécessaire de passer les requêtes à la base en paramètres.

<application-policy name="sewatech">
  <authentication>
    <login-module 
         code="org.jboss.security.auth.spi.DatabaseServerLoginModule"
         flag="required">
      <module-option name="dsJndiName">java:/SwDS</module-option>
      <module-option name="principalsQuery">
        SELECT PASSWD FROM SW_USERS WHERE USERID=?
      </module-option>
      <module-option name="rolesQuery">
        SELECT ROLEID, 'Roles' FROM SW_ROLES WHERE USERID=?
      </module-option>
    </login-module>
  </authentication>
</application-policy>

Le domaine de sécurité peut être spécifié pour chaque EJB, dans le fichier META-INF/ejb-jar.xml ou globalement, pour tous les EJB, dans le fichier conf/standardjboss.xml.

 <jboss> 
   ...
   <container-configuration>
     <container-name>Standard Stateless SessionBean</container-name> 
     ...
     <security-domain>java:/jaas/sewatech</security-domain>
   </container-configuration> 
   ...
 </jboss>

Mise en oeuvre dans BEA Weblogic

Les exemples ci-dessous ont été testés avec BEA Weblogic 9.1. Pour les informations d'identification soient transmises du client vers le serveur, Weblogic supporte JAAS et la technique du contexte JNDI.

Coté client

Contexte JNDI

C'est la technique la plus simple, mais aussi la plus ancienne et la plus limitée. Le username et le password sont transmis avant le lookup au contexte JNDI :

Hashtable props = new Hashtable();
props.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
props.put(Context.PROVIDER_URL, "t3://myserver:7001");
props.put(Context.SECURITY_PRINCIPAL, "alexis");
props.put(Context.SECURITY_CREDENTIAL, "alexis01");

Context ctx = new InitialContext(props);
...

Module JAAS

Comme pour JBoss, il est possible de transmettre au serveur des informations d'identité via JAAS. PAr contre, il y a quelques contrainte en plus par rapport à JBoss.

Tout d'abord, il faut obligatoirement utiliser le module fourni par BEA.

weblo {
  // jBoss LoginModule
  weblogic.security.auth.login.UserPasswordLoginModule  required;
};

Ensuite, il faut obligatoirement utiliser un CallbackHandler qui gère les URLCallback, comme par exemple l'URLCallbackHandler.

   private Subject login() throws LoginException {
       CallbackHandler handler = new URLCallbackHandler(login, password.getBytes(), url);
       // Initialisation du contexte (configuration "weblo")
       LoginContext lc = new LoginContext("jboss", handler);
       // Demande d'authentification
       lc.login();
       // Lecture du sujet authentifié
       return getSubject();
   }

Enfin, l'accès au serveur doit se faire dans une PrivilegedAction, via la classe weblogic.security.Security

   Security.runAs(subject, new PrivilegedAction() {
       public Object run() {
           try {
               Context ctx = new InitialContext();
               MyHome home = ctx.lookup(...);
               ...
           catch (Exception ex) {
               ...
           }
           return null;
       }});

Conclusion

JAAS apporte une solution souple et pratique pour sécuriser des composants J2EE, que ce soient des EJB ou des applications Web. Cependant, des solutions alternatives, basées sur les mêmes principes de configuration externe, avec le même fonctionnement modulaire, existent. Des solutions comme Acegi Security offrent même une souplesse plus importante.