Suresh Payankannur

Sunday, July 31, 2011

Spring Security, LDAP & Active Directory

Earlier this year, I had to integrate the spring-security with Active Directory, for authentication and authorization. Eventhough there were some documentation available, I had to struggle a bit to get this working. So it will be a good idea to share my findings here. I have been looking for the minimal configuration that accomplishes the goal.

Notes:

  • The sAMAccountName attribute in AD stores the user login.
  • The persion tag will populate the the details of the user in org.springframework.security.ldap.userdetails.Person class. This is handy if one needs to get more than the username like first name, last name etc.
  • A number of ldap related tags are not documented well. Refer to the package org.springframework.security.config.ldap for details of the possible tags that can be used in the spring context.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:s="http://www.springframework.org/schema/security"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemalocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
           http://www.springframework.org/schema/security
           http://www.springframework.org/schema/security/spring-security-3.1.xsd">

  
  <s:http>
    <s:intercept-url access="IS_AUTHENTICATED_REMEMBERED" pattern="/secure/**">
      <s:form-login login-page="login.html"
                    default-target-url="/secure/homepage.do"
                    always-use-default-target="true"/>
    <s:logout/>
    </s:intercept-url>
 </s:http>

 <s:ldap-server id="ldap-server"
                url="ldap://host:389/"
                root=""
                manager-dn="cn=xxx,ou=xxx,DC=corp,dc=xxx,dc=com"
                manager-password="xxx"/>

  <s:authentication-manager alias="authenticationManager">
    <s:ldap-authentication-provider
        user-search-filter="(&amp;(objectclass=user)(sAMAccountName={0}))"
        user-search-base="dc=corp,dc=xxx,dc=com"
        group-search-filter="(&amp;(objectclass=group)(member={0}))"
        group-search-base="ou=xxx,dc=corp,dc=xxx,dc=com"
        user-details-class="person"
        role-prefix="none"
        />
  </s:authentication-manager>
</beans>
For example, if one wants to extract the full name of the user:
import org.springframework.security.ldap.userdetails.Person;

    public String getFullName() {
        Person person = (Person) getAuthentication().getPrincipal();
        String[] cn = person.getCn();

        StringBuilder sbuf = new StringBuilder("");
        if (cn != null  &&  cn.length > 0) {
            for (String s : cn) {
                sbuf.append(s).append(" ");
            }
        }
        return sbuf.toString().trim();
    }

Unit Testing

import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;

import static org.mockito.Mockito.*;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:spring-security-context.xml"})
public class AuthenticationTest {
    @Test
    public void testAuth() {
        // Authentication auth = mock(Authentication.class);
        // when(auth.getPrincipal()).thenReturn("rod");
        // when(auth.getCredentials()).thenReturn("koala");
        // Cannot mock authenticator as the ProviderManager checks for
        // an instance of UsrenamePasswordAuthenticationToken. So create
        // a concrete instance.
        Authentication auth = new UsernamePasswordAuthenticationToken("rod",
                                                                      "koala");
        auth = authenticationManager.authenticate(auth);
        assertTrue(auth.isAuthenticated());
    }

    @Autowired
    private AuthenticationManager authenticationManager;
}

Using embedded ldap server

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:s="http://www.springframework.org/schema/security"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemalocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
           http://www.springframework.org/schema/security
           http://www.springframework.org/schema/security/spring-security-3.1.xsd">

  <s:http>
    <s:intercept-url pattern="/secure/**" 
                     access="IS_AUTHENTICATED_REMEMBERED" />
    <s:form-login login-page="/login.html"
                  default-target-url="/secure/homepage.do" 
                  always-use-default-target="true"/>
    <s:logout />
  </s:http>

  <s:ldap-server ldif="classpath:users.ldif" port="33389"/>

  <s:authentication-manager alias="authenticationManager">
    <s:ldap-authentication-provider
        group-search-filter="member={0}"
        group-search-base="ou=groups"
        user-search-base="ou=people"
        user-search-filter="uid={0}"
        user-details-class="person"
        role-prefix="none"/>
  </s:authentication-manager>
</beans>

1 comment:

Blog Archive

Scroll To Top