Thursday, 26 July 2012

Spring Security 3.1 with LDAP Authentication

If you want to control the access to a web application and make it secure within a Spring environment, a good approach is to apply the Microsoft Active Directory (LDAP) authentication.

However, when I tried to apply this approach I found it was not easy to put all the elements together. The online documentation for Spring Security was not clear enough to my understanding and the synchronisation of dependencies not properly explained; I found a good reference on this ComDynamics article, but again with some missing parts.

After hours of research and test I realised that the Spring Security framework allows high flexibility and lets you customise the way you authenticate and secure your web applications. Once I managed to get it all working I decided to write this new entry on my blog to have everything in one place.

Include your dependencies

The next Spring Security 3.1.0.RELEASE dependencies are required in your pom.xml file to perform the security checks:
  • spring-security-core
  • spring-security-config
  • spring-security-web
  • spring-security-ldap

ActiveDirectoryLdapAuthenticationProvider is the core class in Spring Security 3.1 to allow the integration with LDAP, and one of my first problems was that the versions of Spring Security 3.1 I was using did not include this class in their classpaths, so make sure the version of your dependencies includes it (version 3.1.0.RELEASE does).

Apart from these dependencies, you will also need to include the next one:
  • spring-ldap-core (version 1.3.1.RELEASE worked for me)

Configure your files within the WEB-INF folder

In this case, the security configuration is specified in the security.xml file, under WEB-INF, so in order for the security context to be loaded, your web.xml file should look as shown next:

The application configuration needs to also provide the security filter chain, by specifying the set of URLs that will be processed by the security filter. In this case, all the URLs will require to be secured, so the web.xml file should include the following:

Configure your security context

As stated above, the security context in this example will be detailed in the security.xml file (under WEB-INF), which will look like shown in the following snippet:

It is important to describe some of the elements in the security context:
  • ldapActiveDirectoryAuthProvider: This bean will use the core Spring Security 3.1 class to integrate with LDAP: ActiveDirectoryLdapAuthenticationProvider, which accepts a list of parameters as shown in the example
  • Constructor arguments: ${ldap.domain} and ${ldap.url} represent your Active Directory configuration, that is the domain to which the users belong and the url to connect to the LDAP server
  • authoritiesMapper: This bean is needed to filter the roles within the LDAP groups; it will check whether the users belong to certain LDAP groups or not. The bean uses the customised class com.targetapp.webapp.security.ActiveDirectoryGrantedAuthoritiesMapper, which is explained below
  • security:http: This section specifies your login/logout policy, as well as your security zones:
    • form-login, where you detail the login page, the login processing url and the redirections in case of success or failure in the authentication
    • logout, redirection on sucessful logout
    • itercept-url, which allows you to create as many security zones as you need in your web application, specifying the roles that have permission to access each of the security zones; for simplicity in this example there is only one role, ROLE_ADMIN, that has access to all the sites in the web application, but your LDAP configuration could have different roles with access to different zones in your web application
Configure your login page

Your login page should look like the next example:

Make sure that your form uses the login-processing-url specified in the security.xml file, in this case "/j_spring_security_check".

Configure your role mappings

In order to match the nomenclature used in your security context (security.xml) you need to create the customised mapper class that will be used to implement the authoritiesMapper bean previously declared in the security context.

This will imply the creation of two classes:
  • com.targetapp.webapp.security.SecurityContextAuthority

This class implements the Spring interface GrantedAuthority; it provides the set of the roles used in the security context, that will be mapped by the next class
  • com.targetapp.webapp.security.ActiveDirectoryGrantedAuthoritiesMapper

This class implements the Spring interface GrantedAuthoritiesMapper; the method mapAuthorities gets as argument the set of authorities in the LDAP nomenclature which is mapped to the set of authorities defined in the previous class (in the security context nomenclature); the method will provide the ActiveDirectoryLdapAuthenticationProvider class with the list of roles available for the user that is trying to login
At this point you should have all your configuration ready to provide your web application with Active Directory security.

22 comments:

  1. I have created a demo of this but my execution never goes in the ActiveDirectoryGrantedAuthoritiesMapper class at all.
    help me ....
    Many many Thanx

    ReplyDelete
  2. Hi Ravinder, thanks very much for reading this post.

    Could you please give a bit more of information?
    Do you have any logs?
    Does your execution through any exceptions?

    However, one thing that is worth double checking is the version of Spring Security you are using and if it contains the next class in the right path: org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider

    Please let me know.

    ReplyDelete
  3. Hi Julio,

    Thanks for the reply.
    I am using 3.1.0 RELEASE and i have verified the path you provided. It's same.

    When i try to login with correct/incorrect credentials it simple redirect me to error page. Even there is no error in log as well.

    Please guide.

    Thanks.

    ReplyDelete
    Replies
    1. Can you set your logging level to DEBUG mode?

      This should generate more information about the logging process and it should point at the reason why you are being redirected to the error page, or at least you should be able to view the classes that are being used for the login.

      Delete
  4. which ldap server should I download ? Plz guide

    ReplyDelete
  5. I load as roles or user permissions yourself the database?

    ReplyDelete
  6. Hi,

    I am trying thid example and keep getting this error when trying to login:

    java.lang.ClassCastException: org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider cannot be cast to .ActiveDirectoryAuthentication

    Anyone know can help on this?

    Thanks.

    ReplyDelete
    Replies
    1. Could you please give more details?
      Have you checked if you are using the right Spring versions?

      Delete
    2. Hi Julio,

      Thanks. I managed to set it up already. But I am facing with this issue even though I have enter the correct credential.

      2013-07-19 10:29:04,619 [http-bio-8080-exec-4] INFO org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider - Active Directory authentication failed: Supplied password was invalid
      BadCredentialsException: org.springframework.security.authentication.BadCredentialsException: Bad credentials

      Delete
    3. Hello,

      Good to know that you solved the previous issue.

      Did you use the right case-sensitivity?

      Please let me know.

      Delete
    4. Hi Julio,

      Thank you for your reply. What do u mean by right case-senitivity?

      Delete
    5. I'm having the EXACT SAME issue as WenJun. I know it's partially working though because if I enter a nonsense username or incorrect password, I get a log message saying username not found or password not valid. If I use a correct username and password, it gives me the BadCredentailsException

      Delete
    6. Also, I tried all uppercase and all lowercase on my username but it gives the same BadCredentialsException either way. I'm assuming it accepts either because it didn't give me a "username not found" message.

      Delete
    7. Hello again,

      After analysing the issues with WenJun, we found out that the occurred because of the LDAP domain definition (ldap.domain) of the "ldapActiveDirectoryAuthProvider" bean; it has to be done with a fomat like "abc.com" instead of "dc=abc,dc=com".

      Best regards,
      Julio

      Delete
    8. I believe the bug I'm seeing may be a bug in ActiveDirectoryLdapAuthenticationProvider. I found the following forum post on the spring boards:

      http://forum.springsource.org/showthread.php?134991-Active-Directory-authentication-in-Spring-3-1

      And I created a JIRA issue for it here since one didn't seem to exist:

      https://jira.springsource.org/browse/SEC-2224

      Delete
  7. Agreed with Julio. Thank once again for the help render. :)

    Regards,
    WenJun

    ReplyDelete
  8. hi Julio Diego Barrado Fernández

    i am facing the issue as below
    java.lang.ClassNotFoundException: org.springframework.aop.config.AbstractInterceptorDrivenBeanDefinitionDecorator

    i checked that class is present in classpath.. then why this error is coming.plss help me

    ReplyDelete
  9. Hi Julio

    I'm having the same problem: Bad Credentials

    ReplyDelete
  10. Hola Julio, exelente post!, te hago una consulta, como puedo hacer para reeplazar los argumentos:


    la idea es reemplazarlos por datos obtenidos de un properties.
    Muchas gracias,. un saludo.

    ReplyDelete
  11. perdon Julio, los argumentos:

    ${ldap.domain}
    ${ldap.url}

    ReplyDelete
    Replies
    1. Hola Sergio,

      Creo que lo que necesitas hacer es incluir el fichero properties en tu "applicationContext-resources.xml", por ejemplo, si añades un bean como el siguiente:

      < b ean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
      < p roperty name="ignoreUnresolvablePlaceholders" value="true"/>
      < p roperty name="locations">
      < l ist>
      < v alue>classpath:jdbc.properties< / v alue>
      < v alue>classpath:mail.properties< / v alue>
      < v alue>classpath:application.properties< / v alue>
      < / l ist>
      < / p roperty>
      < / b ean>

      Una vez hecho esto, puedes añadir los valores para tus parámetros en el "application.properties".

      Ya me dices si te ha servido mi sugerencia o no.

      Un saludo,
      Julio

      Delete
    2. Hola Julio, exactamente!!, asi lo hice, cree un aplication.properties con:

      ldap.domain=WIN-R747K5VOO12
      ldap.url=ldap://WIN-R747K5VOO12:389

      ahora si al poner un usuario/pass en mi pagina Login.jsp se me conecta bien a mi maquina con un AD corriendo (WIN-R747K5VOO12), PERO, tengo el mismo problema que los muchachos de mas arriba, bad credencials!!, si quieres te pego los archivos de configuracion para ver si puedes dilucidar cual es mi problema!.

      Venga Julio!, me ha sido de mucha utilidad tu post!.
      Un abrazo!!

      Delete