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.
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)
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.