spring-ldap
Spring LDAP Overview
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
<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
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
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
public void authenticate(String username, String password) {
contextSource
.getContext(
"cn=" +
username +
",ou=users," +
env.getRequiredProperty("ldap.partitionSuffix"), password);
}
4.3. User Creation
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
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
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”.