16.2. Modules personnalisés

Si les modules de connexion fournis avec l'infrastructure de sécurité EAP ne fonctionnent pas avec votre environnement de sécurité, vous pouvez écrire votre propre implémentation de module de connexion personnalisée. AuthenticationManager nécessite un modèle d'utilisation particulière des groupes de principaux Subject. Vous devez comprendre les fonctionnalités de stockage des informations de la classe Subject JAAS et l'usage attendu de ces fonctionnalités pour écrire un module de connexion qui fonctionne avec l' AuthenticationManager.
Cette section examine ce besoin et introduit deux extraits d'implémentations de LoginModule qui peuvent vous aider à mettre en place des modules de connexion personnalisés.
Vous pouvez obtenir des informations de sécurité associées à un Subject en utilisant les méthodes suivantes :
java.util.Set getPrincipals()
java.util.Set getPrincipals(java.lang.Class c)
java.util.Set getPrivateCredentials()
java.util.Set getPrivateCredentials(java.lang.Class c)
java.util.Set getPublicCredentials()
java.util.Set getPublicCredentials(java.lang.Class c)
Pour les identités et les rôles Subject EAP a sélectionné le choix le plus logique : les groupes de principaux obtenus via getPrincipals() et getPrincipals(java.lang.Class). Le modèle d'utilisation est le suivant :
  • Les éléments d'identité de l'utilisateur (nom d'utilisateur, numéro de sécurité sociale, ID de l'employé, par exemple;) sont stockés sous forme d'objets java.security.Principal dans les groupements SubjectPrincipals L'application Principal qui représente l'identité de l'utilisateur doit fonder des comparaisons et l'équivalence sur le nom du principal. Une implémentation appropriée est disponible sous forme de classe org.jboss.security.SimplePrincipal. D'autres instances Principal peuvent être ajoutées aux groupements SubjectPrincipals selon les besoins.
  • Les rôles utisateur assignés sont également stockés dans le groupe Principals, et sont groupés dans des ensembles de rôles nommés utilisant les instances java.security.acl.Group. L'interface Group définit une collection de Principal et /ou Group et est une sous-interface de java.security.Principal.
  • Le nombre d'ensembles de rôles qui vous souhaitez peut être assigné à un Subject.
  • Le framework de sécurité EAP utilise deux ensembles de rôles bien connus ayant pour noms Roles et CallerPrincipal.
    • Le groupe Roles est une collection de Principal de rôles nommés tels qu'ils sont connus dans le domaine d'application sous lequel le Subject a été authentifié. L'ensemble de rôles est utilisé par des méthodes comme EJBContext.isCallerInRole(String), que les EJB peuvent utiliser pour voir si l'appelant en cours appartient au rôle de domaine de l'application nommée. La logique d'intercepteur de sécurité qui s'occupe des vérifications des permissions de méthode utilise également cet ensemble de rôles.
    • Le CallerPrincipal Group consiste en une seule identité Principal assignée à l'utilisateur dans le domaine de sécurité. La méthode EJBContext.getCallerPrincipal() utilise le CallerPrincipal pour permettre au domaine d'application de procéder au mappage de l'identité de l'environnement de l'opération à une identité d'utilisateur qui convienne à l'application. Si un Subject ne possède pas de CallerPrincipal Group, l'identité de l'application sera la même que l'identité de l'environnement opérationnel.

16.2.1. Support des modèles d'utilisation de Subject

Pour simplifier une implémentation correcte des modèles d'utilisation de Subject décrite dans Section 16.2, « Modules personnalisés », EAP inclut des modules de connexion qui mettent dans le Subject authentifié un modèle qui oblige une bonne utilisation du Subject.
AbstractServerLoginModule

La classe la plus standard parmi les deux est org.jboss.security.auth.spi.AbstractServerLoginModule.

Procure une implémentation de l'interface javax.security.auth.spi.LoginModule et procure des méthodes abstraties pour les tâches clé spécifiques à une infrastructure de sécurité d'environnement d'opération. Les informations clé de la classe sont expliqués dans Exemple 16.20, « Fragment de classe AbstractServerLoginModule ». La documentation JavaDoc détaille les responsabilités des sous-classes.

Important

La variable de l'instance loginOk est pivotale. Elle doit être définie à true si la connexion réussit, ou à false par des sous-classes qui remplacent la méthode de connexion. Si cette variable n'est pas correcte, la méthode de validation ne mettra pas le sujet à jour correctement.
La vérification les résultats de la phase de connexion permet aux modules de connexion d'êtres enchaînés ensemble par des indicateurs de contrôle. Ces indicateurs de contrôle ne nécessitent pas le succès des modules de connexion au cours du processus d'authentification.

Exemple 16.20. Fragment de classe AbstractServerLoginModule

package org.jboss.security.auth.spi;
/**
 *  This class implements the common functionality required for a JAAS
 *  server-side LoginModule and implements the PicketBox standard
 *  Subject usage pattern of storing identities and roles. Subclass
 *  this module to create your own custom LoginModule and override the
 *  login(), getRoleSets(), and getIdentity() methods.
 */
public abstract class AbstractServerLoginModule
    implements javax.security.auth.spi.LoginModule
{
    protected Subject subject;
    protected CallbackHandler callbackHandler;
    protected Map sharedState;
    protected Map options;
    protected Logger log;

    /** Flag indicating if the shared credential should be used */
    protected boolean useFirstPass;
    /** 
     * Flag indicating if the login phase succeeded. Subclasses that
     * override the login method must set this to true on successful
     * completion of login
     */
    protected boolean loginOk;
                
    // ...
    /**
     * Initialize the login module. This stores the subject,
     * callbackHandler and sharedState and options for the login
     * session. Subclasses should override if they need to process
     * their own options. A call to super.initialize(...)  must be
     * made in the case of an override.
     *
     * <p>
     * The options are checked for the  <em>password-stacking</em> parameter.
     * If this is set to "useFirstPass", the login identity will be taken from the
     * <code>javax.security.auth.login.name</code> value of the sharedState map,
     * and the proof of identity from the
     * <code>javax.security.auth.login.password</code> value of the sharedState map.
     *
     * @param subject the Subject to update after a successful login.
     * @param callbackHandler the CallbackHandler that will be used to obtain the
     * the user identity and credentials.
     * @param sharedState a Map shared between all configured login module instances
     * @param options the parameters passed to the login module.
     */
    public void initialize(Subject subject,
                           CallbackHandler callbackHandler,
                           Map sharedState,
                           Map options)
    {
        // ...
    }
    

    /**
     *  Looks for javax.security.auth.login.name and
     *  javax.security.auth.login.password values in the sharedState
     *  map if the useFirstPass option was true and returns true if
     *  they exist. If they do not or are null this method returns
     *  false.  
     *  Note that subclasses that override the login method
     *  must set the loginOk var to true if the login succeeds in
     *  order for the commit phase to populate the Subject. This
     *  implementation sets loginOk to true if the login() method
     *  returns true, otherwise, it sets loginOk to false.
     */
    public boolean login() 
        throws LoginException
    {
        // ...
    }
    
    /**
     *  Overridden by subclasses to return the Principal that
     *  corresponds to the user primary identity.
     */
    abstract protected Principal getIdentity();
                
    /**
     *  Overridden by subclasses to return the Groups that correspond
     *  to the role sets assigned to the user. Subclasses should
     *  create at least a Group named "Roles" that contains the roles
     *  assigned to the user.  A second common group is
     *  "CallerPrincipal," which provides the application identity of
     *  the user rather than the security domain identity.
     * 
     *  @return Group[] containing the sets of roles
     */
    abstract protected Group[] getRoleSets() throws LoginException;
}
UsernamePasswordLoginModule

Le second module de connexion basé abstract pour les modules de connexion personnalisés est le module org.jboss.security.auth.spi.UsernamePasswordLoginModule.

Ce module de connexion simplifie encore plus l'implémentation du module de connexion personnalisé par un nom d'utilisateur basé sur une chaîne comme identité d'utilisateur et un mot de passe char[] comme informations d'authentification. Il prend également en charge le mappage des utilisateurs anonymes (indiqué par un nom d'utilisateur et un mot de passe null) à un principal sans rôle. Les informations clé de la classe sont mises en évidence dans le fragment de classe suivant. Les commentaires JavaDoc détaillent les responsabilités des sous-classes.

Exemple 16.21. Fragment de classe UsernamePasswordLoginModule

package org.jboss.security.auth.spi;

/**
 *  An abstract subclass of AbstractServerLoginModule that imposes a
 *  an identity == String username, credentials == String password
 *  view on the login process. Subclasses override the
 *  getUsersPassword() and getUsersRoles() methods to return the
 *  expected password and roles for the user.
 */
public abstract class UsernamePasswordLoginModule
    extends AbstractServerLoginModule
{
    /** The login identity */
    private Principal identity;
    /** The proof of login identity */
    private char[] credential;
    /** The principal to use when a null username and password are seen */
    private Principal unauthenticatedIdentity;

    /**
     * The message digest algorithm used to hash passwords. If null then
     * plain passwords will be used. */
    private String hashAlgorithm = null;

    /**
     *  The name of the charset/encoding to use when converting the
     * password String to a byte array. Default is the platform's
     * default encoding.
     */
     private String hashCharset = null;

    /** The string encoding format to use. Defaults to base64. */
    private String hashEncoding = null;
                
    // ...
                
    /** 
     *  Override the superclass method to look for an
     *  unauthenticatedIdentity property. This method first invokes
     *  the super version.
     *
     *  @param options,
     *  @option unauthenticatedIdentity: the name of the principal to
     *  assign and authenticate when a null username and password are
     *  seen.
     */
    public void initialize(Subject subject,
                           CallbackHandler callbackHandler,
                           Map sharedState,
                           Map options)
    {
        super.initialize(subject, callbackHandler, sharedState,
                         options);
        // Check for unauthenticatedIdentity option.
        Object option = options.get("unauthenticatedIdentity");
        String name = (String) option;
        if (name != null) {
            unauthenticatedIdentity = new SimplePrincipal(name);
        }
    }
                
    // ...
                
    /**
     *  A hook that allows subclasses to change the validation of the
     *  input password against the expected password. This version
     *  checks that neither inputPassword or expectedPassword are null
     *  and that inputPassword.equals(expectedPassword) is true;
     *
     *  @return true if the inputPassword is valid, false otherwise.
     */
    protected boolean validatePassword(String inputPassword,
                                       String expectedPassword)
    {
        if (inputPassword == null || expectedPassword == null) {
            return false;
        }
        return inputPassword.equals(expectedPassword);
    }
    
    /**
     *  Get the expected password for the current username available
     * via the getUsername() method. This is called from within the
     * login() method after the CallbackHandler has returned the
     * username and candidate password.
     *
     * @return the valid password String
     */
    abstract protected String getUsersPassword()
        throws LoginException;
}
Sous-classement des modules de connexion

Le choix de sous-classement du AbstractServerLoginModule versus UsernamePasswordLoginModule est basé sur le fait de savoir si des informations d'identification et un nom d'utilisateur basés chaîne sont utilisables avec la technologie d'authentification pour laquelle vous écrivez le module de connexion. Si la sémantique basée chaîne est valide, alors ce sera la sous classe UsernamePasswordLoginModule, sinon, ce sera la sous-classe AbstractServerLoginModule.

Étapes de sous-classement

Les étapes que votre module de connexion personnalisée doit s'exécuter dépendent de la classe de module de base de connexion que vous choisissez. Lorsque vous écrivez un module de connexion personnalisé qui s'intègre à votre infrastructure de sécurité, vous devriez commencer en sous-classant AbstractServerLoginModule ou UsernamePasswordLoginModule pour faire en sorte que votre module de connexion fournisse les informations de Principal authentifié dans la forme prévue par le gestionnaire de sécurité EAP.

Lors du sous-classement de AbstractServerLoginModule, vous devrez remplacer ce qui suit :
  • void initialize(Subject, CallbackHandler, Map, Map): si vous avez des options personnalisées pour le traitement.
  • boolean login() : utilisé pour effectuer l'authentification. Veillez bien à définir l'instance loginOk sur true si la connexion réussit, sur false si elle échoue.
  • Principal getIdentity() : pour retourner l'objet Principal pour l'utilisateur authentifié dans l'étape log().
  • Group[] getRoleSets() : utilisé pour retourner au moins un Group nommé Roles qui contienne les rôles assignés au Principal authentifié durant le login(). Un second Group commun se nomme CallerPrincipal et il procure l'identité de l'application de l'utilisateur au lieu de l'identité du domaine de sécurité.
Quand vous sous-classez UsernamePasswordLoginModule, vous devrez remplacer ce qui suit :
  • void initialize(Subject, CallbackHandler, Map, Map): si vous avez des options personnalisées pour le traitement.
  • Group[] getRoleSets() : utilisé pour retourner au moins un Group nommé Roles qui contienne les rôles assignés au Principal authentifié durant le login(). Un second Group commun se nomme CallerPrincipal et il procure l'identité de l'application de l'utilisateur au lieu de l'identité du domaine de sécurité.
  • String getUsersPassword() : pour retourner un mot de passe pour le nom d'utilisateur en cours, via la méthode getUsername(). La méthode getUsersPassword() est appelée à partir de login() une fois que le callbackhandler a retourné un nom d'utlisateur et un mot de passe de candidat.