Spring Security est un projet de Spring Source anciennement appelé Acegi Security. Le framework a beaucoup évolué depuis en s'étoffant de nombreux connecteurs et en améliorant son intégration avec Spring framework.
Dans cet article, nous allons aborder la méthode d'authentification LDAP (Windows Active Directory, Open LDAP...) à travers Spring Security. Les détails de l'utilisation de l'autorisation (droit d'effectuer certaines opérations) ne seront pas évoqués ici, le sujet étant déjà largement documenté. La référence du framework comprend tout un chapitre dédié à l'interaction avec un LDAP.
Les annuaires LDAP
Un annuaire LDAP est un système de gestion des informations associées aux acteurs d'un SI (utilisateurs, machines, équipements électroniques...). Le principal rôle de l'annuaire est de stocker les informations des utilisateurs et de permettre grâce à des mécanismes simples de rechercher, tri et organiser l'information.
Les annuaires LDAP proposent une organisation hiérarchique de l'information. Chaque élément est contenu dans les éléments parents (groupes, unités organisationnelles ou physiques...).
La fonctionnalité qui nous intéresse est la possibilité d'authentifier les utilisateurs. L'annuaire LDAP possède le login de l'utilisateur et le password (hashé), il peut ainsi authentifier de manière sûre un utilisateur. Les annuaires ont aussi la possibilité d'enregistrer une liste de rôles attribués aux utilisateurs. Ces rôles permettent de différencier des profils d'utilisateurs pour les offrir des autorisations différentes.
LDAP n'est pas une implémentation d'annuaire, mais uniquement une norme respectée par de nombreux grands systèmes d'annuaires (Windows Active Directory, Open LDAP, Apache Directory Server, Novell eDirectory, IBM Lotus Domino...).
Dernier point d'importance, LDAP dispose d'une règle de nommage permettant de gérer les organisations hiérarchiques.
Par exemple, le Distinguished Name (DN) cn=Jean,ou=gens,dc=EXEMPLE,dc=FR
est composé des éléments suivants :
- CN (common name) = jean ⇒ Le nom de l'utilisateur
- OU (Organisation Unit) = gens ⇒ Le groupe contenant les utilisateurs (ou=machines pour les PC par exemple...)
- DC (Domain component) dc=EXEMPLE,dc=FR ⇒ exemple.fr, le domaine du ldap
Le DN d'un utilisateur permet de l'identifier de façon unique dans l'annuaire.
Le principe de l'authentification sur LDAP
Le processus d'authentification sur un LDAP s'appelle un BIND. Il permet de fournir au système LDAP un DN d'utilisateur et un mot de passe (généralement chiffré).
Avant de pouvoir faire un bind, il est nécessaire de récupérer le DN de l'utilisateur. Il n'est pas réaliste de demander à l'utilisateur son DN complet. La première étape est donc généralement une recherche dans l'annuaire sur un attribut (login de l'utilisateur) dans un répertoire de l'annuaire (les utilisateurs généralement).
Une fois le bind réalisé, il est possible de demander des informations supplémentaires à l'annuaire comme par exemple les rôles de l'utilisateur (administrateur, membre d'une liste de diffusion...). Ces éléments pourront être réutilisés dans Spring Security pour automatiser l'attribution d'autorisations.
Exemple d'utilisation
L'exemple suivant montre comment intégrer l'authentification ldap dans un projet comprenant déjà une authentification simple (BDD par exemple) basée sur Spring Security.
L'AuthenticationManager de Spring Security
Ici on configure un authentication-manager grâce à un schéma xml spring security spécialisé. La balise authentication-manager crée automatiquement l'object authentication manager avec une configuration standard efficace. Les enfants de cette balise sont les différentes sources d'authentification utilisée :
- L'authentification provider ldap (ldapAuthProvider)
- Un autre authentification provider définissant un sel et un service spécialisé (proxyUserDetailsService2)
<authentication-manager alias="authenticationManager">
<authentication-provider ref="ldapAuthProvider"></authentication-provider>
<authentication-provider user-service-ref="proxyUserDetailsService2">
<password-encoder ref="passwordEncoder">
<salt-source ref="saltSource"></salt-source>
</password-encoder>
</authentication-provider>
</authentication-manager>
Le LDAP AuthenticationProvider
L'objet authenticationProvider est un service SpringSecurity prenant en en entrée un token (par exemple couple login/password) et répondant avec un utilisateur authentifié (ou null s'il n'existe pas). Dans notre cas, l'authentificationProvider à utiliser est créé avec la configuration xml suivante :
<bean class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider"
id="ldapAuthProvider">
<constructor-arg>
<bean class="org.springframework.security.ldap.authentication.BindAuthenticator">
<constructor-arg ref="contextSource" />
<property name="userSearch">
<bean class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
<constructor-arg value="OU=MonGroup">
<constructor-arg value="(sAMAccountName={0})">
<constructor-arg ref="contextSource">
</bean>
</property>
</bean>
</constructor-arg>
<constructor-arg>
<bean class="org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator">
<constructor-arg ref="contextSource">
<constructor-arg value="OU=Groups,OU=Mycompany,OU=CompanyUsers">
<property name="rolePrefix" value="LDAP_ROLE_" />
</bean>
</constructor-arg>
<property name="userDetailsContextMapper">
<bean class="com.company.AcmeContextMapper">
<property name="AcmeSecurityProviderService" ref="acmeSecurityProviderService" />
</bean>
</property>
</bean>
<bean class="org.springframework.security.ldap.DefaultSpringSecurityContextSource"
id="contextSource">
<constructor-arg value="ldap://1.2.3.4:389/dc=exemple,dc=com" />
<property name="userDn" value="CN=browser,OU=testAccount,DC=exemple,DC=com" />
<property name="password" value="P@ssw0rd" />
</bean>
Le premier bean définit le service d'authentification. Le premier argument (le bind authenticator) définit les paramètres de la recherche de l'utilisateur dans le LDAP (quel groupe cherche, quelle requête...).
La seconde partie du bean (DefaultLdapAuthoritiesPopulator) définit la recherche des groupes LDAP pour les transformer en rôles Spring security.
La dernière partie (userDetailsContextMapper) est une classe qui permet de convertir l'objet User retournée par le LDAP en un objet User de l'application pour l'enrichir, effectuer des modifications...
Le second bean présente le contexte LDAP avec l'url de connexion et la définition d'un utilisateur permettant de lire les informations du LDAP.
A retenir
La connexion LDAP est une authentification en deux étapes (recherche puis bind) qui permet d'authentifier l'utilisateur et de lui attribuer des rôles. Cette solution permet de mettre en oeuvre rapidement une authentification centralisée.
Les informations utiles pour configurer une connexion LDAP
- L'URL de connexion au LDAP
- Un utilisateur ayant les droits pour browser les comptes LDAP :
- son DN
- son password
- L'OU contenant les utilisateurs
- La requêtes de recherche de l'utilisateur [sur un active directory (sAMAccountName={0}) ]
- Pour la transformation des groupes en rôles :
- l'OU contenant les groupes
- Un préfixe pour les rôles
Article rédigé pour la plateforme de capitalisation technique de Sword