• JBoss custom login module : un example

    Dans ce tutoriel nous allons voir écrire et déployer un module de login personnalisé pour JBoss Application Server.

    En effet même si JBoss est livré avec quelques modules de login très pratiques, ils ne sont pas tous adaptés à toutes les situation. C’est notamment le cas avec la structure de certains annuaires LDAP ou dans le cas de système d’authentification étranges.

    Le Chapitre 8 de la documentation de JBoss est très bien faite à ce sujet, et il est recommandé de le lire avant de vous lancer dans l’écriture d’un module de login.

    Notre méthode d’authentification
    Dans cet exemple nous allons implémenter une méthode d’authentification volontairement étrange :
    - les noms d’utilisateurs seron dynamiques et sont de la forme userX où X est un nombre décimal
    - les noms d’administrateurs sont de la forme adminY où Y est n’importe quoi
    - un nom d’utilisateur pour les connections anonymes
    - les mots de passes des utilisateurs sont de la userX loves you
    - les mots de passes des administrateurs sont de la forme adminY is the boss
    - les utilisateurs anonymes n’ont pas de mot de passe

    Un module configurable
    Il est important que ce module soit configurable (en dehors du code source bien entendu) !
    Comme paramètre de configuration du module, nous voulons :
    - une expression rationnelle pour le nom d’utilisateur anonyme (optionel, “anonymous” par défaut)
    - une expression rationnelle pour les noms d’utilisateurs et d’administrateurs
    - un modèles pour les mots de passes d’utilisateurs et d’administrateurs

    Configuration de JBoss
    Le fichier par défaut de JBoss contenant la configuration des modules de login est ${JBOSS_HOME}/server/default/conf/login-config.xml.
    Chaque module de login JBoss doit avoir un nom de domaine de sécurité JAAS. Nous allons appeler le nôtre “MyLoginModule”
    Il nous faut ajouter le code suivant juste avant le domaine de sécurité “other” :

    com.blogger.xtechteam.examples.jboss.loginmodule.CustomJBossLoginModule
    est le nom de notre classe pour le module de login.

    Le module de login
    Notre classe de module de login doit être dérivée de org.jboss.security.auth.spi.UsernamePasswordLoginModule.
    En dérivant de cette classe il vous reste à implémenter les méthodes suivantes :
    - initialize() : appelée par JBoss juste après la création d’une nouvelle instancce du module.
    - validatePassword() : appelée par JBoss à chaque fois qu’un utilisateur essayer de valider son mot de passe.
    - getRoleSets() : appelée par JBoss après qu’un utilisateur soit correctement authentifié de façon à récupérer la liste des rôles de l’utilisateur.

    Voici le code du module de login :


    001 public class CustomJBossLoginModule extends UsernamePasswordLoginModule {
    002
    003 /* Regular expressions used for user login name */
    004 private Pattern guestRegEx;
    005 private Pattern userRegEx;
    006 private Pattern adminRegEx;
    007 /* Magic phrases used in place of user password */
    008 private String userMagic;
    009 private String adminMagic;
    010
    011 /* Currently authenticated user roles */
    012 private boolean isGuest;
    013 private boolean hasUserRole;
    014 private boolean hasAdminRole;
    015
    016 /* called by JBoss when loading the login module */
    017 @Override
    018 public void initialize (
    019 Subject subject,
    020 CallbackHandler callbackHandler,
    021 Map sharedState,
    022 Map options) {
    023 super.initialize (subject, callbackHandler, sharedState, options);
    024 log.info (“CustomJBossLoginModule : initialize”);
    025
    026 /* load the optional login module option */
    027 String guestParam = (String)options.get (“guestRegEx”);
    028 if (guestParam == null) {
    029 guestParam = “anonymous”;
    030 }
    031
    032 /* load the mandatory login module options */
    033 guestRegEx = Pattern.compile (guestParam);
    034 userRegEx = Pattern.compile ((String)options.get (“userRegEx”));
    035 adminRegEx = Pattern.compile ((String)options.get (“adminRegEx”));
    036 userMagic = (String)options.get (“userMagic”);
    037 adminMagic = (String)options.get (“adminMagic”);
    038 }
    039
    040 public CustomJBossLoginModule () {
    041 super ();
    042 }
    043
    044 /* called by JBoss to validate a user password */
    045 @SuppressWarnings (“unchecked”)
    046 @Override
    047 protected boolean validatePassword (String inputPassword, String expectedPassword) {
    048 log.info (“CustomJBossLoginModule : validatePassword for ” + getUsername ());
    049
    050 /* validate the user name */
    051 isGuest = guestRegEx.matcher (getUsername ()).matches ();
    052 if (!isGuest) {
    053 hasUserRole = userRegEx.matcher (getUsername ()).matches ();
    054 if (!hasUserRole) {
    055 hasAdminRole = adminRegEx.matcher (getUsername ()).matches ();
    056 }
    057 }
    058
    059 /* validate the password */
    060 boolean validPassword = isGuest;
    061 if (hasUserRole) {
    062 validPassword = inputPassword.equals (
    063 String.format (userMagic, getUsername ()));
    064 } else if (hasAdminRole) {
    065 validPassword = inputPassword.equals (
    066 String.format (adminMagic, getUsername ()));
    067 }
    068
    069 /* save some informations for the application */
    070 if (validPassword) {
    071 Set
    072 principals = subject.getPrincipals ();
    073 principals.add (new SimplePrincipal (getUsername ()));
    074 principals.add (new SimplePrincipal (inputPassword));
    075 principals.add (new SimplePrincipal (“Hello from CustomJBossLoginModule”));
    076 principals.add (new SimplePrincipal (Boolean.toString (isGuest)));
    077 principals.add (new SimplePrincipal (Boolean.toString (hasUserRole)));
    078 principals.add (new SimplePrincipal (Boolean.toString (hasAdminRole)));
    079 log.info (String.format (“CustomJBossLoginModule : user %s authenticated (guest : %b, user : %b, admin : %b)”,
    080 getUsername (), isGuest, hasUserRole, hasAdminRole));
    081 } else {
    082 log.info (“CustomJBossLoginModule : Invalid credentials”);
    083 }
    084 return validPassword;
    085 }
    086
    087 @Override
    088 protected String getUsersPassword () throws LoginException {
    089 return “”;
    090 }
    091
    092 /* fill the role collection */
    093 @Override
    094 protected Group[] getRoleSets () throws LoginException {
    095 SimpleGroup userRoles = new SimpleGroup (“Roles”);
    096 if (isGuest) {
    097 userRoles.addMember (new SimplePrincipal (“Guest”));
    098 }
    099 if (hasUserRole) {
    100 userRoles.addMember (new SimplePrincipal (“User”));
    101 }
    102 if (hasAdminRole) {
    103 userRoles.addMember (new SimplePrincipal (“Admin”));
    104 }
    105 Group[] roleSets = { userRoles };
    106 return roleSets;
    107 }
    108
    109 }

    Compilation
    Pour compiler ce module de login vous aurez besion de deux archives .jar de JBoss : jboss-common.jar et jbosssx.jar.

    Deploiment
    Une fois compilé placer le module dans une archives .jar et copier cette archive dans le répertoire ${JBOSS_HOME}/server/default/lib.

    Utiliser les informations de connextion dans l’application
    Vous avez (normalement!) du noter que notre module enregistre des informations concernant l’utilisateur dans la collection de principals du subject (l’utilisateur authentifié).
    Cette collection est utilisée pour pouvoir récupérer les informations de connexion depuis les applications webs, et ce de façon portable (comprenez : indépendant du serveur d’applications).
    Voici un exemple de classe qui peut être utilisé pour la récupération de ces informations :


    01 public class AuthenticatedUserInformations {
    02 
    03 public class InvalidUserException extends Exception {};
    04 
    05 /** The JACC PolicyContext key for the current Subject */
    06 private static final String SUBJECT_CONTEXT_KEY =
    07 "javax.security.auth.Subject.container";
    08 
    09 private String username;
    10 private String password;
    11 private String hello;
    12 private boolean isGuest;
    13 private boolean isAdmin;
    14 private boolean isUser;
    15 
    16 public AuthenticatedUserInformations () throws InvalidUserException {
    17 Subject caller;
    18 try {
    19 caller = (SubjectPolicyContext.getContext (SUBJECT_CONTEXT_KEY);
    20 if (caller == null) {
    21 throw new InvalidUserException ();
    22 }
    23 Iterator
    24 it = caller.getPrincipals ().iterator ();
    25 username = it.next ().getName ();
    26 password = it.next ().getName ();
    27 hello = it.next ().getName ();
    28 isGuest = Boolean.valueOf (it.next ().getName ());
    29 isUser = Boolean.valueOf (it.next ().getName ());
    30 isAdmin = Boolean.valueOf (it.next ().getName ());
    31 catch (PolicyContextException e) {
    32 throw new InvalidUserException ();
    33 }
    34 }
    35 
    36 public String getUsername () {
    37 return username;
    38 }
    39 
    40 public String getPassword () {
    41 return password;
    42 }
    43 
    44 public boolean isGuest () {
    45 return isGuest;
    46 }
    47 
    48 public boolean isAdmin () {
    49 return isAdmin;
    50 }
    51 
    52 public boolean isUser () {
    53 return isUser;
    54 }
    55 
    56 }

    Exemple d’application web
    Enfin, nous allons écrire l’application pour tester notre module de login.
    Premièrement, le descripteur de déploiement web.xml doit contenir les éléments classiques de sécurité :

    Ensuite, le descripteur jboss-web.xml doit déclarer notre domaine de sécurité :


    Enfin, voici une page JSP simple pour démontrer l’utilisation de notre classe AuthenticatedUserInformations.
    Ajoutez les lignes suivantes dans une page JSP vide :


    et quelques lignes de JSP et d’HTML dans le corps de la page :

    Déployez votre application et testez !

Haut de page