Access Keys:
Skip to content (Access Key - 0)

LDAP Authentication

Labels

security security Delete
ldap ldap Delete
acegi acegi Delete
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.

Table of content


AppFuse 2.1.x + Spring Security 3.x

With AppFuse 2.1.x comes a new version of Spring Security, exactly, the 3.0.4.RELEASE, and the working mode has changed since 2.x. This means that the latest solution to work with LDAP doesn't work.

Here you can find the new approach.

Adding dependencies to the pom.xml

In the pom.xml, add the following dependencies

	<dependency> 
		<groupId>org.springframework.security</groupId> 
		<artifactId>spring-security-ldap</artifactId> 
		<version>${spring.security.version}</version> 
	</dependency>
        <dependency>
            <groupId>org.springframework.ldap</groupId>
            <artifactId>spring-ldap-core</artifactId>
            <version>${spring.ldap.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.ldap</groupId>
            <artifactId>spring-ldap-core-tiger</artifactId>
            <version>${spring.ldap.version}</version>
        </dependency>

And the corresponding variable at the end:

        <spring.ldap.version>1.3.1.RELEASE</spring.ldap.version>
Configuring the security.xml file
The default userDao authentication provider

You can comment it to have only one type of authentication, or you can leave it to have a chaining auth, first LDAP and after the userDao validation.

    <authentication-manager>    	    
        <authentication-provider user-service-ref="userDao">
            <password-encoder ref="passwordEncoder"/>
        </authentication-provider>  
        
       	<authentication-provider ref="ldapAuthProvider"/>
    </authentication-manager>
Add the LDAP server
   <ldap-server id="ldapServer"
                url="ldap://localhost:389/dc=example,dc=com";
                manager-dn="cn=Manager,dc=example,dc=com"
                manager-password="pass"/>

If you don't specify the manager-dn and manager-password the connection will
be anonymous.

The authorization with custom database provider

You need to add the following beans:

    <beans:bean id="ldapAuthProvider" class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">
		<beans:constructor-arg ref="ldapBindAuthenticator"/> 
		<beans:constructor-arg ref="ldapAuthoritiesPopulator"/> 
		<!-- beans:property name="userDetailsContextMapper" ref="ldapUserDetailsContextMapper"/ -->
	</beans:bean>

	<beans:bean id="ldapBindAuthenticator" class="org.springframework.security.ldap.authentication.BindAuthenticator">
		<beans:constructor-arg ref="ldapServer"/> 
		<beans:property name="userSearch" ref="ldapSearchBean"/>
	</beans:bean>

	<beans:bean id="ldapSearchBean" class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
		<beans:constructor-arg value=""/><!-- user-search-base --> 
		<beans:constructor-arg value="(uid={0})"/> <!-- user-search-filter --> 
		<beans:constructor-arg ref="ldapServer"/>
	</beans:bean>

   	<beans:bean id="cppAuthoritiesUserDetailsServiceImpl" class="cat.urv.cpp.webapp.util.CppAuthoritiesUserDetailsServiceImpl">
     		<beans:constructor-arg ref="userDao"/>
   	</beans:bean>
   	
	<beans:bean id="ldapAuthoritiesPopulator" 
		class="org.springframework.security.ldap.authentication.UserDetailsServiceLdapAuthoritiesPopulator">
		<beans:constructor-arg ref="cppAuthoritiesUserDetailsServiceImpl"/>
	</beans:bean>

With the class CppAuthoritiesUserDetailsServiceImpl you indicates what role have a user. The source code of this class is:

package cat.urv.cpp.webapp.util;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

import cat.urv.cpp.dao.UserDao;
import cat.urv.cpp.model.User;

public class CppAuthoritiesUserDetailsServiceImpl implements UserDetailsService {

    private final transient Log log = LogFactory.getLog(CppAuthoritiesPopulator.class);
    private UserDao userDao;
	
    @Autowired    
	public CppAuthoritiesUserDetailsServiceImpl(UserDao userDao) {
		this.userDao = userDao;
	}
    
	public UserDetails loadUserByUsername(String username)
			throws UsernameNotFoundException, DataAccessException {
		User user = (User) userDao.loadUserByUsername(username);
		return user;
	}
}
In case of problems... activate the debug

One recommendation, you can configure the log4j.xml file to see what's happening in the spring security environment:

    <logger name="org.springframework.security">
        <level value="DEBUG"/>
    </logger>
    
    <logger name="org.springframework.ldap">
        <level value="DEBUG"/>
    </logger>

AppFuse 2.0.x + Spring Security 2.x

This part is taken from a thread on the AppFuse user list.

Adding dependencies to the pom.xml

In the pom.xml, add the following dependencies

<dependencies>
...
        <dependency>
            <groupId>org.springframework.ldap</groupId>
            <artifactId>spring-ldap-core</artifactId>
            <version>${spring.ldap.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.ldap</groupId>
            <artifactId>spring-ldap-core-tiger</artifactId>
            <version>${spring.ldap.version}</version>
        </dependency>
...
</dependencies>

And the corresponding variable at the end:

        <spring.ldap.version>1.3.0.RELEASE</spring.ldap.version>
Configuring the security.xml file
The default userDao authentication provider

You can comment it to have only one type of authentication, or you can leave it to have a chaining auth, first LDAP and after the userDao validation.

    <authentication-provider user-service-ref="userDao">
        <password-encoder ref="passwordEncoder"/>
    </authentication-provider>
Add the LDAP server
   <ldap-server id="ldapServer"
                url="ldap://localhost:389/dc=example,dc=com";
                manager-dn="cn=Manager,dc=example,dc=com"
                manager-password="pass"/>

If you don't specify the manager-dn and manager-password the connection will
be anonymous.

The authorization

Configure the binding procedure (how ldap will do the autentication) and the populate procedure (how ldap will do the autorization, with this configuration you need to have a cn property in the LDAP to map the correct roles inside the application).

Database authorization with a custom populator

    <beans:bean id="userSearch"
	    class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
	  <beans:constructor-arg index="0" value=""/>
	  <beans:constructor-arg index="1" value="(uid={0})"/>
	  <beans:constructor-arg index="2" ref="ldapServer" />
	</beans:bean>
    
    <beans:bean id="ldapAuthenticationProvider"
	        class="org.springframework.security.providers.ldap.LdapAuthenticationProvider" autowire="default">
	  <custom-authentication-provider/>
	  <beans:constructor-arg>
	    <beans:bean class="org.springframework.security.providers.ldap.authenticator.BindAuthenticator">
	      <beans:constructor-arg ref="ldapServer"/>
	      <beans:property name="userDnPatterns">
	        <beans:list><beans:value>uid={0}</beans:value></beans:list>
	      </beans:property>
	      <beans:property name="userSearch" ref="userSearch"/>
	    </beans:bean>
	  </beans:constructor-arg>
	   
	   <beans:constructor-arg>
	   	<beans:bean class="cat.urv.pdd3.webapp.util.Pdd3AuthoritiesPopulator">
      		<beans:constructor-arg ref="userDao"/>
	   	</beans:bean>
	   </beans:constructor-arg>
	</beans:bean>

You can create your custom Populator, in case of you want to have the mapping logic about what role have one user.

package cat.urv.custom.webapp.util;

import java.util.HashSet;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.security.GrantedAuthority;
import org.springframework.security.GrantedAuthorityImpl;
import org.springframework.security.ldap.LdapAuthoritiesPopulator;

import cat.urv.custom.dao.UserDao;
import cat.urv.custom.model.Role;
import cat.urv.custom.model.User;
import cat.urv.custom.service.UserManager;

public class CustomAuthoritiesPopulator implements LdapAuthoritiesPopulator {

    private final transient Log log = LogFactory.getLog(CustomAuthoritiesPopulator.class);
    private UserDao userDao;
    
    @Autowired
    
	public CustomAuthoritiesPopulator(UserDao userDao) {
		this.userDao = userDao;
	}

	public GrantedAuthority[] getGrantedAuthorities(
			DirContextOperations userData, String username) {
	
		Set<GrantedAuthority> userPerms = new HashSet<GrantedAuthority>();
		
		User user = (User) userDao.loadUserByUsername(username);
		Set<Role> roles = user.getRoles();
		
		//get users permissions from service
		for (Role perm : roles) {
				userPerms.add(new GrantedAuthorityImpl(perm.getName()));
		}
		return (GrantedAuthority[]) userPerms.toArray(new GrantedAuthority[userPerms.size()] );
	}
}

LDAP authorization from attribute

You can also be able to authorize one user using an attribute of the LDAP repository:

    <beans:bean id="ldapAuthenticationProvider"
	        class="org.springframework.security.providers.ldap.LdapAuthenticationProvider" autowire="default">
	  <custom-authentication-provider/>
	  <beans:constructor-arg>
	    <beans:bean class="org.springframework.security.providers.ldap.authenticator.BindAuthenticator">
	      <beans:constructor-arg ref="ldapServer"/>
	      <beans:property name="userDnPatterns">
	        <beans:list><beans:value>uid={0}</beans:value></beans:list>
	      </beans:property>
	      <beans:property name="userSearch" ref="userSearch"/>
	    </beans:bean>
	  </beans:constructor-arg>
	   
	  <beans:constructor-arg>
	    <beans:bean class="org.springframework.security.ldap.populator.DefaultLdapAuthoritiesPopulator">
	      <beans:constructor-arg ref="ldapServer"/>
	      <beans:constructor-arg value="ou=People"/>
	      <beans:property name="groupRoleAttribute" value="cn"/>	     
	    </beans:bean>
	  </beans:constructor-arg> 
	</beans:bean>
In case of problems... activate the debug

One recommendation, you can configure the log4j.xml file to see what's happening in the spring security environment:

    <logger name="org.springframework.security">
        <level value="DEBUG"/>
    </logger>
    
    <logger name="org.springframework.ldap">
        <level value="DEBUG"/>
    </logger>

AppFuse 1.9.4 + Acegi Security

This part is taken from a thread on the AppFuse user list.

Here's what Matt has done in the past to get LDAP working with AppFuse 1.9.4. The same concepts should be applicable to AppFuse 2.0.x.

1. Change the "authenticationManager" bean to use "ldapProvider"
instead of "daoAuthenticationProvider":

   <bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager">
       <property name="providers">
           <list>
               <ref local="ldapProvider"/>
               <!--ref local="daoAuthenticationProvider"/-->
               <ref local="anonymousAuthenticationProvider"/>
               <ref local="rememberMeAuthenticationProvider"/>
           </list>
       </property>
   </bean>

2. Added ldapProvider and supporting beans:

   <bean id="ldapProvider" class="org.acegisecurity.providers.ldap.LdapAuthenticationProvider">
       <constructor-arg>
           <bean class="org.acegisecurity.providers.ldap.authenticator.BindAuthenticator">
               <constructor-arg ref="initialDirContextFactory"/>
               <property name="userDnPatterns">
                   <list>
                       <value>uid={0}</value>
                   </list>
               </property>
               <property name="userSearch" ref="userSearch"/>
               <property name="userDetailsMapper" ref="ldapUserDetailsMapper"/>
           </bean>
       </constructor-arg>
       <constructor-arg>
           <bean class="org.acegisecurity.providers.ldap.populator.DefaultLdapAuthoritiesPopulator">
               <constructor-arg ref="initialDirContextFactory"/>
               <constructor-arg value=""/>
               <property name="groupRoleAttribute" value="cn"/>
               <property name="groupSearchFilter"
value="(&amp;(objectclass=groupOfUniqueNames)(uniqueMember={0}))"/>
               <property name="searchSubtree" value="true"/>
               <property name="rolePrefix" value=""/>
               <property name="convertToUpperCase" value="false"/>
           </bean>
       </constructor-arg>
   </bean>

   <bean id="initialDirContextFactory" class="org.acegisecurity.ldap.DefaultInitialDirContextFactory">
       <constructor-arg value="${ldap.url}/${ldap.base}"/>
       <property name="managerDn" value="${ldap.username}"/>
       <property name="managerPassword" value="${ldap.password}"/>
   </bean>

   <bean id="userSearch" class="org.acegisecurity.ldap.search.FilterBasedLdapUserSearch">
       <constructor-arg index="0" value=""/>
       <constructor-arg index="1" value="(uid={0})"/>
       <constructor-arg index="2" ref="initialDirContextFactory"/>
       <property name="searchSubtree" value="true"/>
   </bean>

   <bean id="ldapUserDetailsMapper" class="org.acegisecurity.userdetails.ldap.LdapUserDetailsMapper">
       <property name="rolePrefix" value=""/>
   </bean>

3. Change the passwordEncoder bean to be LdapShaPasswordEncoder:

<bean id="passwordEncoder" class="org.acegisecurity.providers.ldap.authenticator.LdapShaPasswordEncoder"/>

In this example, my ldap.properties (which populates initialDirContextFactory) is set to:

ldap.url=ldap://localhost:1389
ldap.base=ou=system
ldap.username=uid=admin,ou=system
ldap.password=secret 
Adaptavist Theme Builder (4.0.0-M8) Powered by Atlassian Confluence 3.1, the Enterprise Wiki.