The new Certification Class of Learn Spring Security is out:

>> CHECK OUT THE COURSE

1. Overview

LDAP directory servers are read-optimized hierarchical data stores. Typically, they’re used for storing user-related information required for user authentication and authorization.

In this article, we’ll explore the Spring LDAP APIs to authenticate and search for users, as well as to create and modify users in the directory server. The same set of APIs can be used for managing any other type of entries in LDAP.

2. Maven Dependencies

Let’s begin by adding the required Maven dependency:

<dependency>
    <groupId>org.springframework.ldap</groupId>
    <artifactId>spring-ldap-core</artifactId>
    <version>2.3.1.RELEASE</version>
</dependency>

The latest version of this dependency can be found at spring-ldap-core.

3. Data Preparation

For the purpose of this article, let’s first create the following LDAP entry:

ou=users,dc=example,dc=com (objectClass=organizationalUnit)

Under this node, we will create new users, modify existing users, authenticate existing users and search for information.

4. Spring LDAP APIs

4.1. ContextSource & LdapTemplate Bean Definition

ContextSource is used for creating the LdapTemplate. We will see the use of ContextSource during user authentication in the next section:

@Bean
public LdapContextSource contextSource() {
    LdapContextSource contextSource = new LdapContextSource();
    
    contextSource.setUrl(env.getRequiredProperty("ldap.url"));
    contextSource.setBase(
      env.getRequiredProperty("ldap.partitionSuffix"));
    contextSource.setUserDn(
      env.getRequiredProperty("ldap.principal"));
    contextSource.setPassword(
      env.getRequiredProperty("ldap.password"));
    
    return contextSource;
}

LdapTemplate is used for creation and modification of LDAP entries:

@Bean
public LdapTemplate ldapTemplate() {
    return new LdapTemplate(contextSource());
}

4.2. User Authentication

Let’s now implement a simple piece of logic to authenticate an existing user:

public void authenticate(String username, String password) {
    contextSource
      .getContext(
        "cn=" + 
         username + 
         ",ou=users," + 
         env.getRequiredProperty("ldap.partitionSuffix"), password);
}

4.3. User Creation

Next, let’s create a new user and store an SHA hash of the password in LDAP.

At the time of authentication, the LDAP server generates the SHA hash of the supplied password and compares it to the stored one:

public void create(String username, String password) {
    Name dn = LdapNameBuilder
      .newInstance()
      .add("ou", "users")
      .add("cn", username)
      .build();
    DirContextAdapter context = new DirContextAdapter(dn);

    context.setAttributeValues(
      "objectclass", 
      new String[] 
        { "top", 
          "person", 
          "organizationalPerson", 
          "inetOrgPerson" });
    context.setAttributeValue("cn", username);
    context.setAttributeValue("sn", username);
    context.setAttributeValue
      ("userPassword", digestSHA(password));

    ldapTemplate.bind(context);
}

digestSHA() is a custom method which returns the Base64 encoded string of the SHA hash of the supplied password.

Finally, the bind() method of LdapTemplate is used to create an entry in the LDAP server.

4.4. User Modification

We can modify an existing user or entry with the following method:

public void modify(String username, String password) {
    Name dn = LdapNameBuilder.newInstance()
      .add("ou", "users")
      .add("cn", username)
      .build();
    DirContextOperations context 
      = ldapTemplate.lookupContext(dn);

    context.setAttributeValues
      ("objectclass", 
          new String[] 
            { "top", 
              "person", 
              "organizationalPerson", 
              "inetOrgPerson" });
    context.setAttributeValue("cn", username);
    context.setAttributeValue("sn", username);
    context.setAttributeValue("userPassword", 
      digestSHA(password));

    ldapTemplate.modifyAttributes(context);
}

The lookupContext() method is used to find the supplied user.

4.5. User Search

We can search for existing users using search filters:

public List<String> search(String username) {
    return ldapTemplate
      .search(
        "ou=users", 
        "cn=" + username, 
        (AttributesMapper<String>) attrs -> (String) attrs.get("cn").get());
}

The AttributesMapper is used to get the desired attribute value from the entries found. Internally, Spring LdapTemplate invokes the AttributesMapper for all the entries found and creates a list of the attribute values.

5. Testing

spring-ldap-test provides an embedded LDAP server based on ApacheDS 1.5.5. To setup the embedded LDAP server for testing, we need to configure the following Spring bean:

@Bean
public TestContextSourceFactoryBean testContextSource() {
    TestContextSourceFactoryBean contextSource 
      = new TestContextSourceFactoryBean();
    
    contextSource.setDefaultPartitionName(
      env.getRequiredProperty("ldap.partition"));
    contextSource.setDefaultPartitionSuffix(
      env.getRequiredProperty("ldap.partitionSuffix"));
    contextSource.setPrincipal(
      env.getRequiredProperty("ldap.principal"));
    contextSource.setPassword(
      env.getRequiredProperty("ldap.password"));
    contextSource.setLdifFile(
      resourceLoader.getResource(
        env.getRequiredProperty("ldap.ldiffile")));
    contextSource.setPort(
      Integer.valueOf(
        env.getRequiredProperty("ldap.port")));
    return contextSource;
}

Let’s test our user search method with JUnit:

@Test
public void 
  givenLdapClient_whenCorrectSearchFilter_thenEntriesReturned() {
    List<String> users = ldapClient
      .search(SEARCH_STRING);
 
    assertThat(users, Matchers.containsInAnyOrder(USER2, USER3));
}

6. Conclusion

In this article, we have introduced Spring LDAP APIs and developed simple methods for user authentication, user search, user creation and modification in an LDAP server.

As always the full source code is available in this Github project. The tests are created under Maven profile “live” and hence can be run using the option “-P live”.

Go deeper into Spring Security with the course:

>> LEARN SPRING SECURITY

Sort by:   newest | oldest | most voted
Theodor Woland
Guest

How to configure connection to my AD (win server 2003) server in that code? (using LDAP)

I know username, password, ip. Where should I paste them?

Grzegorz Piwowarek
Guest

Theodor, in this case you need to configure ActiveDirectoryLdapAuthenticationProvider bean. AD will not work with the standard LDAP one

Theodor Woland
Guest

Can I ask you to set an example for my case?

User info for login:
UserDn: cn=Jack Wood,cn=users,dc=example,dc=local
User pass:1234

Grzegorz Piwowarek
Guest

Theodor, in order to setup ActiveDirectoryLdapAuthenticationProvider, you need to pass URL and DOMAIN in the contructor. You did not provide an URL

Theodor Woland
Guest

ip:192.168.56.101:389

Eugen Paraschiv
Guest
Hey Theodor, I also wanted to jump in here with a quick note. Typically, you’d know the credentials/IP of your LDAP server. In your case, you probably need to ask your admin to figure out exactly where that’s running and how to connect to it. I also want to add that, if you’re not dealing with the typical case, it’s usually much better to reach out to the support of that technology directly (google group, mailing list, or whatever that might be). That’s going to be a better way to get answers for corner cases and scenarios that are more… Read more »
Theodor Woland
Guest

Hi Eugen!
I’m doing my Course work, so I’m my own administrator. :)))
I have VM Virtualbox with win serv 2003.
And my task is connect to it from host OS and manage users.
Could you give some advice? (how to configure spring-ldap for connection)

Eugen Paraschiv
Guest

Unfortunately I’ve never had to do that, so I’m afraid I can’t give you any concrete advice.
My suggestion is to figure out exactly what host/port the LDAP server is running, and then use that info when you’re setting up your Spring LDAP bean.
But, again – this is general advice, and you might run into very specific problems there – which is why I suggested asking over on their mailing list, or perhaps on StackOverflow.
Best of luck setting everything up.
Cheers,
Eugen.

wpDiscuz