LDAP Authentication using Spring Security
Recently, the project I was working on, required implementation of simple login functionality, where users were supposed to be authenticated against a remote Active Directory via LDAP. It was a maven project based on spring framework so we implemented the application security using the spring security framework.
Problem Statement
The Active Directory had users stored in a tree-structure which looked similar to the below example.
The Distinguished Name (DN) of the directory were like the following one -
CN=admin user,OU=Users,OU=Prod,DC=tst,DC=nl,DC=eur,DC=local
As we know that DN is used to uniquely identify a particular user entry, here the DN is formed using the user name and the application required the authentication to be done on the basis of combination of the user id (which is attributed to sAMAccountName, as shown in the example below) and user password.
Any particular user entry looked like this -
Implemented Solution
The login functionality was implemented using the Bind LDAP Authentication method in which we first bind the application to the directory using an admin user and then users can be searched based on the search criteria as provided.
Below, I have summarized the steps to implement LDAP authentication using the spring security framework with 6 simple steps, which are as:
- Adding maven dependency for spring security
- Adding security configuration for authentication
- Adding authorization roles
- Configuring web.xml to activate URL filtering
- Adding login page
- Adding logout and blocking caching
First of all, add the below dependencies to the pom.xml in your application.
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-ldap</artifactId>
<version>3.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>3.1.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>3.0.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${spring.version}</version>
<type>pom</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>3.0.7.RELEASE</version>
<type>pom</type>
<scope>compile</scope>
</dependency>
Next, add a file security-context.xml to your project which contains the LDAP configuration required to connect and authenticate with the active directory.
The http block specifies the access level to different url patterns and also the login & logout actions. The authentication provider tag refers to ldapProvider for authentication base, which is the implementation object for LDAP Bind authentication built using the contextSource, userSearch and CustomAuthoritiesPopulator objects. The contextSource is the actual reference to the active directory which specifies the LDAP url and user DN, used to bind to the directory. The userSearch signfies the seach criteria to be used to search any particular user once the bind has been performed. The CustomAuthoritiesPopulator is used to define the authorized roles and has been discussed in the next section.
<http auto-config="true" use-expressions="true">
<intercept-url pattern="/resources/**" access="permitAll" />
<intercept-url pattern="/login" access="permitAll" />
<intercept-url pattern="/**" access="hasRole('ROLE_USER')" />
<form-login login-processing-url="/j_spring_security_check" login-page="/login" authentication-failure-url="/login?loginError=t" default-target-url="/dashboard" />
<logout invalidate-session="true" logout-success-url="/login" logout-url="/logout" />
</http>
<authentication-manager>
<authentication-provider ref="ldapProvider" />
</authentication-manager>
<beans:bean id="contextSource" class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
<beans:constructor-arg value="ldap://tst.nl.eur.local:389/" />
<beans:property name="userDn" value="CN=admin user,OU=Users,OU=Prod,DC=tst,DC=nl,DC=eur,DC=local" />
<beans:property name="password" value="password" />
</beans:bean>
<beans:bean id="ldapProvider" class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">
<beans:constructor-arg>
<beans:bean class="org.springframework.security.ldap.authentication.BindAuthenticator">
<beans:constructor-arg ref="contextSource" />
<beans:property name="userSearch" ref="userSearch" />
</beans:bean>
</beans:constructor-arg>
<beans:constructor-arg>
<beans:bean class="com.tst.web.controller.CustomAuthoritiesPopulator" />
</beans:constructor-arg>
</beans:bean>
<beans:bean id="userSearch" class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
<!-- searchBase, searchFilter, contextSource -->
<beans:constructor-arg index="0" value="OU=Users,OU=Prod,DC=tst,DC=nl,DC=eur,DC=local" />
<beans:constructor-arg index="1" value="sAMAccountName={0}" />
<beans:constructor-arg index="2" ref="contextSource" />
</beans:bean>
In the above file, we first do a ldap bind operation using the admin user and then we do a search in the directory using the field sAMAccountName as the search criteria. The {0} indicates that user id entered by used would be substituted in its place.
Instead of providing the admin user and password in the default context, you may also read it from a property file, as-
value="${ldap.admin.user.dn}"
value="${ldap.admin.user.password}"
Then, you need to add a java class, as CustomAuthoritiesPopulator.java, to define the authoritizations.
public class CustomAuthoritiesPopulator implements LdapAuthoritiesPopulator {
public Collection<GrantedAuthority> getGrantedAuthorities(DirContextOperations arg0, String arg1) {
ArrayList<GrantedAuthority> list = new ArrayList<GrantedAuthority>();
list.add((new SimpleGrantedAuthority("ROLE_USER")));
return list;
}
}
Next, add filter to the web.xml and also load the security-context.xml to the context configuration.
<context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/security-context.xm</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>DispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>DispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <filter> <filter-name>springSecurityFilterChain</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>springSecurityFilterChain</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
Now, we add the login.jsp to our application which uses the predefined objects corresponding to the spring security framework. It also includes an if statement to display a login error message, in case logout attempt fails.
<form action="j_spring_security_check" name="loginForm" method="post">
<table id="login">
<thead>
<tr><th colspan="2" align="center"><h3>Login</h3></th></tr>
</thead>
<tbody>
<c:if test="${param.loginError == 't'}">
<tr><td colspan="2">Invalid UserId/password</td></tr>
</c:if>
<tr>
<td><label class="inputLabel" for="j_username">User Id:</label></td>
<td><input type='text' name='j_username' value='' id="j_username"></td>
</tr>
<tr>
<td><label class="inputLabel" for="j_password">Password:</label></td>
<td><input type='password' name='j_password' value='' id="j_password"></td>
</tr>
<tr>
<td><input name="submit" type="submit" value='Login'/></td>
<td><input name="reset" type="reset" id="resetButton" /></td>
</tr>
</tbody>
</table>
</form>
Finally, provide the logout action as below on the dashboard.jsp (which happens to be the landing page for the user after a successful login attempt).
<a href='<spring:url value="/logout" htmlEscape="true"/>'>logout</a>
Sometimes, it may happen that once the user has logged in successfully, he is able to view the dashboard page by hitting the url directly even after doing a logout. This may happen due to browser caching which depends on the browser solely. However, this can be avoided using a meta tag as -
<meta http-equiv="Pragma" content="no-cache">
Hope this information helps building security in your application.
Filed under: Spring




This is awesome! Made my day! looking for this simple example and found nowhere but here!
Hi, I like your instructions but Im a little lost here since Im knew to all this Im trying this out at work and Im getting errors do I have to change the url in the <beans:constructor-arg value="ldap://tst.nl.eur.local:389/" /> example to my localhost? Or can I use the values stated in this example? Thanks for posting this its really informative as well as easy to understand.
Hi yoviluz
The string 'tst.nl.eur.local' represents the ldap server name while 389 is the default port. You will have to use the connection string as per the ldap server you are trying to connect to.
Thanks I will try that now
okay sorry to bother again but…Im using tomcat to do this sample and Im not sure if I have to download an ldap server or if I can configure tomcat to do this I tried to download and connect but it gives me errors using a free ldap server any suggustions?