Les concepts d'authentification dans GlassFish

L'objectif recherché dans cet article est de présenter les mécanismes d'authentification proposés dans GlassFish, en s'arrêtant en particulier sur les concepts de types d'authentification, de realms, de groupes et de rôles utilisateurs.
La littérature spécialisée sur le sujet est souvent confuse et peu explicite. Aussi, j'espère que la lecture de cet article vous aidera à vous poser les bonnes questions et à appréhender plus sereinement la mise en oeuvre technique de l'authentification dans GlassFish.

La version de GlassFish à laquelle je me suis référé pour rédiger cet article est la 3.1.2.

Les types d'authentification

Pour sécuriser l'accès à ses ressources protégées, une application déployée dans le conteneur de servlets Glassfish peut s'appuyer sur l'un des 4 types d'authentification présentés ci-dessous.

  • Authentification par mot de passe :
    • BASIC : la boîte de connexion fournie en standard par le navigateur internet est présentée à l'utilisateur lorsqu'il demande à accéder à une ressource protégée. L'identifiant de connexion et le mot de passe qu'il a saisi sont transmis en clair (encodés en base 64 pour être exact) dans la requête HTTP envoyée au serveur web (voici un exemple de boîte de connexion affichée dans Firefox et dans Chrome).
    • FORM : une page personnalisée et intégrée à la charte graphique de l'application web développée est affichée pour la saisie de l'identifiant et du mot de passe lors de l'accès à une ressource protégée (voici un exemple de page de connexion personnalisée).
    • DIGEST : ce type d'authentification est similaire au type BASIC excepté que l'identifiant de connexion et le mot de passe ne sont pas transmis entre le client et le serveur. A la place, c'est une clé calculée côté client par hachage MD5 à partir de l'identifiant et du mot de passe, qui est transmise au serveur. Ce dernier calcule à son tour la même clé, la compare à la clé reçue et en fonction valide ou non l'accès à la ressource protégée.
  • Authentification par certificat :
    • CLIENT-CERT : le client est authentifié à partir de son certificat de clé publique, que l'on pourrait assimiler à un passeport électronique attestant l'identité de l'utilisateur qui demande à accéder à des ressources protégées du serveur. Ce certificat est délivré par une Autorité de Certification (AC ou CA en anglais pour Certificate Authority) qui garantie l'identité de son titulaire.

L'identifiant et le mot de passe étant transmis en clair dans la requêtre HTTP pour les types d'authentification BASE et FORM, il est particulièrement recommandé que la page de connexion soit accédée en SSL (HTTPS) pour une mise en oeuvre sécurisée de l'application en production, en particulier si elle est accessible sur internet.

La déclaration de ce type d'authentification pour une application web est à réaliser dans son descripteur de déploiement WEB-INF/web.xml, par l'intermédiaire de la balise <auth-method/> comme illustré ci-dessous :

<login-config>
        <auth-method>FORM</auth-method>
</login-config>
Fic. 1 : déclaration du type d'authentification FORM dans le descripteur de déploiement web.xml

A la découverte des realms...

Un realm (ou un domaine en Français), est une base de données des utilisateurs habilités à utiliser une ou plusieurs applications déployées sur un même serveur web. Le realm peut contenir les mots de passe de ses utilisateurs et leurs groupes d'appartenance.

Les groupes d'utilisateurs renseignés dans un realm font référence à une population d'utilisateurs présentant des caractéristiques communes au sein d'une organisation.

Chaque utilisateur d'un realm peut appartenir à un seul groupe, plusieurs groupes voire à aucun groupe. Le terme Principal est également utilisé pour désigner l'utilisateur dans un realm.

Dans Glassfish, un realm peut être stocké dans un fichier plat (classe FileRealm), dans une base de données relationnelle de type MySQL (JDBCRealm) ou dans une base de données de certificats (CertificateRealm).
Le realm peut également faire référence à un annuaire LDAP (LDAPRealm), à la base des utilisateurs déclarés au niveau système d'exploitation des systèmes Unix tels que Solaris (SolarisRealm), Linux ou MacOS (PamRealm).
Enfin, un realm sur mesure peut être développé pour répondre à des besoins spéciques d'authentification (se référer au Guide de développement d'application de GlassFish pour en savoir plus).

La configuration d'un realm s'effectue dans le serveur d'application GlassFish par l'intermédiaire de sa Console d'Administration, en ligne de commande via l'outil asadmin ou par la mise à jour directe du fichier de configuration domain.xml de GlassFish.

glassfish new realm Fig. 1 - Ajout d'un realm depuis la console d'administration de Glassfish.

Pour ajouter un realm, vous devez lui attribuer un nom (champ Name:), choisir la classe correspondant à l'un des types de realm supportés (liste déroulante Classe Name:) et renseigner les valeurs pour chacune des propriétés spécifiques à la classe sélectionnée.
Parmi ces propriétés, la propriété JAAS Context est à renseigner obligatoirement quelque soit la classe de realm sélectionnée.
Le tableau ci-dessous synthétise par classe de realm, la ou les valeurs possibles de la propriété JAAS Context ainsi que les types d'authentification qui peuvent lui être associés :

Classe realm Propriété JAAS Context Type d'authentification
com.sun.enterprise.security.auth.realm.pam.PamRealm pamRealm BASIC, FORM et DIGEST
com.sun.enterprise.security.auth.realm.solaris.SolarisRealm solarisRealm BASIC, FORM et DIGEST
com.sun.enterprise.security.auth.realm.certificate.CertificateRealm certificateRealm CLIENT-CERT
com.sun.enterprise.security.auth.realm.jdbc.JDBCRealm jdbcRealm BASIC et FORM
jdbcDigestRealm DIGEST
com.sun.enterprise.security.auth.realm.file.FileRealm fileRealm BASIC, FORM et DIGEST
com.sun.enterprise.security.auth.realm.ldap.LDAPRealm ldapRealm BASIC, FORM et DIGEST
Tab. 1 - Valeurs de la propriété JAAS Context des classes de realm supportées par GlassFish.

Une fois le realm créé, son nom est indiqué dans le descripteur de déploiement WEB-INF/web.xml de l'application à l'intérieur de la balise <realm-name/> pour permettre au conteneur de servlets de savoir quel realm consulter pour authentifier les utilisateurs.
Ci-dessous un exemple de déclaration du realm de type fileRealm nommé salesInvoicesRealm pour une authentification de type FORM :

<login-config>
        <auth-method>FORM</auth-method>
        <realm-name>salesInvoicesRealm</realm-name>
</login-config>
Fic. 2 : déclaration d'un realm dans le descripteur de déploiement web.xml

GlassFish est pré-configuré en standard avec 3 realms nommés file, admin-realm et certificate. Le realm file est celui utilisé par défaut si aucun realm n'est indiqué dans le descripteur de déploiement WEB-INF/web.xml pour la balise <realm-name/>. Le realm admin-realm est quant à lui dédié à l'authentification des administrateurs de GlassFish.

Autorisations et Rôles

Les autorisations d'une application web sont étroitement liées au mécanisme d'authentification. Ces deux concepts ne peuvent être dissociés et c'est ce que je tenterai de montrer dans la suite de ce paragraphe.

La notion de Rôle...

Dans GlassFish, la configuration des autorisations d'accès des utilisateurs à une application web repose sur la notion de rôle.
Un rôle correspond à un profil type d'utilisateurs dont les autorisations au sein d'une application peuvent être limitées à un sous-ensemble de ressources, de fonctionnalités et de données.

Authentification et Autorisations

Dans une application web développée en Java, la question des autorisations se pose dès lors que l'utilisateur tente d'accéder à une ressource sécurisée. Par ressource, on entend les pages JSP, les vues Facelet, les servlets, les images et tous fichiers de l'application (scripts Javascript, documents PDF...).
En effet, l'activation de l'authentification pour une application web requiert obligatoirement la configuration des URL à sécuriser (balise <url-pattern/> sous <security-constraint/>) et des rôles autorisés pour ces URL (balise <auth-constraint/>).

Dans l'extrait du fichier WEB-INF/web.xml ci-dessous, sont déclarés sous la balise <security-constraint/> les 3 rôles autorisés à accéder à toutes les ressources d'URL /secure/*. Tout rôle renseigné sous la balise <auth-constraint/> doit être également déclaré sous la balise <security-role/>.

    <security-constraint>
        <web-resource-collection>
            <web-resource-name>secure-pages</web-resource-name>
            <url-pattern>/secure/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>security_manager</role-name>
            <role-name>settings_manager</role-name>
            <role-name>accounting_manager</role-name>
        </auth-constraint>
    </security-constraint>
    <security-role>
        <role-name>security_manager</role-name>
    </security-role>
    <security-role>
        <role-name>settings_manager</role-name>
    </security-role>
    <security-role>
        <role-name>accounting_manager</role-name>
    </security-role>
Fic. 3 : déclaration des rôles autorisés à accéder aux ressources d'URL /secure/* dans le fichier web.xml.

En définitive, on ne peut pas activer l'authentification pour une application web si l'on ne configure pas les autorisations d'accès aux ressources de l'application.
Ainsi, l'authentification d'un utilisateur ne peut réussir :

  • que si l'utilisateur fournit la preuve de son identité (authentification par mot de passe ou par certificat),
  • et seulement si l'utilisateur dispose du rôle autorisé à accéder aux ressources demandées.

On comprend alors lorsqu'on a connaissance de ces éléments, que la mise en oeuvre d'un mécanisme d'authentification dans GlassFish ne peut se faire sans la définition d'autorisations à travers les rôles.

Autorisations sous le contrôle de l'application web

Après une authentification réussie, l'application web peut gérer les autorisations à un niveau encore plus fin que le niveau ressources, en limitant par exemple les fonctionnalités à disposition de l'utilisateur dans la page JSP ou la vue Facelet (par exemple le profil A autorise la consultation des factures mais pas leur modification ni suppression) ou encore les données accessibles (par exemple l'utilisateur de profil B ne peut consulter que les factures de sa direction mais pas celles des autres directions). L'application web gère les autorisations à accorder à un utilisateur authentifié en s'appuyant sur la méthode isUserInRole(String role) de l'objet HttpServletRequest.

Mise en relation des utilisateurs et des rôles

Les notions de rôle et de groupe sont clairement séparées dans GlassFish et par conséquent, une mise en relation des groupes d'utilisateurs d'un realm avec les rôles d'une application web est nécessaire pour que cette dernière identifie le rôle de l'utilisateur une fois authentifié.
Cette correspondance entre groupes et rôles est à effectuer dans le descripteur de déploiement de GlassFish WEB-INF/glassfish-web.xml par l'emploi de la balise <security-role-mapping/>, comme cela est montré ci-dessous.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE glassfish-web-app PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Servlet 3.0//EN" "http://glassfish.org/dtds/glassfish-web-app_3_0-1.dtd">
<glassfish-web-app error-url="">
  <security-role-mapping>
    <role-name>security_manager</role-name>
    <group-name>security_manager_group</group-name>
  </security-role-mapping>
  <security-role-mapping>
    <role-name>accounting_manager</role-name>
    <group-name>accounting_manager_group</group-name>
  </security-role-mapping>
  <security-role-mapping>
    <role-name>settings_manager</role-name>
    <group-name>settings_manager_group</group-name>
  </security-role-mapping>
</glassfish-web-app>
Fic. 4 : mise en relation de groupes utilisateurs d'un realm avec les rôles d'une application web dans le fichier glassfish-web.xml.

Dans GlassFish, l'utilisateur déclaré dans un realm peut être directement mis en relation avec un ou plusieurs rôles de l'application web, sans passer par la notion de groupe d'utilisateurs. Dans ce cas, l'association d'un utilisateur à un rôle s'effectue par l'emploi de la balise <principal-name/>.

Autorisations simplifiées

Une application utilisée par un seul profil d'utilisateurs n'a pas besoin de gérer d'autorisations (et donc de rôles), mais simplement d'authentifier ses utilisateurs.
Pour autant, l'activation du mécanisme d'authentification doit obligatoirement passer par la définition à minima d'un rôle pour l'URL des ressources web soumises à un accès restreint.

Concernant la mise en relation des utilisateurs du realm avec l'unique rôle en question, GlassFish propose l'option Default Principal To Role Mapping disponible sur la page Security de la Console d'Administration.
Si cette option est cochée et qu'aucune mise en relation Groupes vers Rôles ou Utilisateurs vers Rôles n'existe dans le descripteur de déploiement glassfish-web.xml, alors GlassFish établit automatiquement la relation entre les utilisateurs du realm et les rôles de l'application au moment du déploiement de l'application web.

Ce qu'il faut retenir...

Pour faire la synthèse de tout ce qui a été présenté dans cet article, il est important de retenir que la mise en oeuvre d'un mécanisme d'authentification pour une application web déployée dans GlassFish nécessite :
  1. De choisir un type d'authentification parmi les types BASIC, FORM, DIGEST ou CLIENT-CERT.
  2. D'identifier le realm auquel GlassFish doit se référer pour authentifier les utilisateurs de l'application web. Les utilisateurs du realm peuvent être stockés dans un fichier plat, une base de données relationnelle ou encore provenir d'un annuaire LDAP ou d'une base d'utilisateurs déclarés au niveau du système d'exploitation Solaris, Linux ou MacOS. Un realm sur mesure peut également être développé pour répondre à des besoins spécifiques d'authentification.
  3. De définir les rôles autorisant l'accès aux différentes ressources de l'application web. Un rôle au minimum doit être configuré pour activer l'authentification d'une application web.
  4. D'établir la relation entre les utilisateurs du realm et les rôles de l'application. Cette mise en correspondance est généralement réalisée par l'intermédiaire des groupes utilisateurs. GlassFish laisse néanmoins la possibilité d'attribuer directement les rôles aux utilisateurs de l'application, sans utiliser la notion de groupes utilisateurs.

Articles connexes