Spring Security + Spring LDAP Authentication Configuration Example
In this tutorial we demonstrate how to Configure Spring Security + Spring LDAP authentication application. We show how to configure spring security and spring LDAP using Java And XML Configuration.
LDAP is often used by organisations as a central repository for user information and as an authentication service. It can also be used to store the role information for application users.
Project Structure
Let’s start by looking at our project structure.
Maven Dependencies
We use Apache Maven to manage our project dependencies. Make sure the following dependencies reside on your class-path.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.memorynotfound.spring.security</groupId>
<artifactId>ldap-auth</artifactId>
<version>1.0.0-SNAPSHOT</version>
<url>https://memorynotfound.com</url>
<name>Spring SECURITY - ${project.artifactId}</name>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.7.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ldap</groupId>
<artifactId>spring-ldap-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-ldap</artifactId>
</dependency>
<dependency>
<groupId>com.unboundid</groupId>
<artifactId>unboundid-ldapsdk</artifactId>
</dependency>
<!-- testing -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
Spring LDAP + Spring Boot Embedded LDAP Configuration
In this example we configure an embedded ldap server. We can configure the LDAP server using the application.yml
file located in the src/main/resources
folder.
# application.yml
spring:
ldap:
# Spring LDAP
#
# In this example we use an embedded ldap server. When using a real one,
# you can configure the settings here.
#
# urls: ldap://localhost:12345
# base: dc=memorynotfound,dc=com
# username: uid=admin
# password: secret
# Embedded Spring LDAP
embedded:
base-dn: dc=memorynotfound,dc=com
credential:
username: uid=admin
password: secret
ldif: classpath:schema.ldif
port: 12345
validation:
enabled: false
Populate LDAP with LDIF
We can populate the embedded LDAP server using a .ldif
file. The following file populates the embedded LDAP server with organizational units, persons and groups. Passwords are hashed using SHA. You can generate your password hash and place it in the userPassword
field.
dn: dc=memorynotfound,dc=com
objectclass: top
objectclass: domain
objectclass: extensibleObject
dc: memorynotfound
dn: ou=groups,dc=memorynotfound,dc=com
objectclass: top
objectclass: organizationalUnit
ou: groups
dn: ou=people,dc=memorynotfound,dc=com
objectclass: top
objectclass: organizationalUnit
ou: people
dn: uid=john,ou=people,dc=memorynotfound,dc=com
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: John Doe
uid: john
userPassword: {SHA}5en6G6MezRroT3XKqkdPOmY/BfQ=
dn: cn=developers,ou=groups,dc=memorynotfound,dc=com
objectclass: top
objectclass: groupOfUniqueNames
cn: developers
ou: developer
uniqueMember: uid=john,ou=people,dc=memorynotfound,dc=com
dn: cn=managers,ou=groups,dc=memorynotfound,dc=com
objectclass: top
objectclass: groupOfUniqueNames
cn: managers
ou: manager
uniqueMember: uid=john,ou=people,dc=memorynotfound,dc=com
Spring Security + Spring LDAP Java Configuration
The @EnableWebSecurity
turns on a variety of beans needed to use Spring Security.
Using the ldapAuthentication()
method, we can configure where spring security can pull the user information from. In this case we set the userDnPatterns()
to uid={0},ou=people
which translates in an LDAP lookup uid={0},ou=people,dc=memorynotfound,dc=com
in the LDAP server. The groupSearchBase()
method is used to map the LDAP groups into roles. Also, the passwordCompare() method configures the encoder and the name of the password’s attribute.
package com.memorynotfound.ldap.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.encoding.LdapShaPasswordEncoder;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import java.util.Collections;
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/managers").hasRole("MANAGERS")
.antMatchers("/employees").hasRole("EMPLOYEES")
.anyRequest().fullyAuthenticated()
.and()
.formLogin();
}
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.ldapAuthentication()
.userDnPatterns("uid={0},ou=people")
.userSearchBase("ou=people")
.userSearchFilter("uid={0}")
.groupSearchBase("ou=groups")
.groupSearchFilter("uniqueMember={0}")
.contextSource(contextSource())
.passwordCompare()
.passwordEncoder(new LdapShaPasswordEncoder())
.passwordAttribute("userPassword");
}
@Bean
public DefaultSpringSecurityContextSource contextSource() {
return new DefaultSpringSecurityContextSource(
Collections.singletonList("ldap://localhost:12345"), "dc=memorynotfound,dc=com");
}
}
Spring Security + Spring LDAP XML Configuration
This is the equivalent Spring Security LDAP XML Configuration.
@ImportResource("classpath:spring-security-config.xml")
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<http auto-config="true" use-expressions="true">
<intercept-url pattern="/managers" access="hasRole('MANAGERS')"/>
<intercept-url pattern="/employees" access="hasRole('EMPLOYEES')"/>
<intercept-url pattern="/**" access="isFullyAuthenticated()"/>
<form-login
default-target-url="/"
always-use-default-target="true" />
</http>
<authentication-manager>
<ldap-authentication-provider
user-dn-pattern="uid={0},ou=people"
user-search-base="ou=people"
user-search-filter="uid={0}"
group-search-base="ou=groups"
group-search-filter="uniqueMember={0}">
<password-compare
hash="{sha}"
password-attribute="userPassword"/>
</ldap-authentication-provider>
</authentication-manager>
<beans:bean class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
<beans:constructor-arg value="ldap://localhost:12345"/>
<beans:constructor-arg value="dc=memorynotfound,dc=com"/>
</beans:bean>
</beans:beans>
Secured Rest Controller
For testing purposes, we created a simple Rest Service. This service is only available for fully authenticated users. We can obtain the logged in user information using the SecurityContextHolder.getContext().getAuthentication()
method which returns a UsernamePasswordAuthenticationToken
instance containing the user information.
package com.memorynotfound.ldap.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.ldap.userdetails.LdapUserDetailsImpl;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HomeController {
private static Logger log = LoggerFactory.getLogger(HomeController.class);
@GetMapping("/")
public String index() {
log.info("Getting UsernamePasswordAuthenticationToken from SecurityContextHolder");
UsernamePasswordAuthenticationToken authentication =
(UsernamePasswordAuthenticationToken)
SecurityContextHolder.getContext().getAuthentication();
log.info("Getting principal from UsernamePasswordAuthenticationToken");
LdapUserDetailsImpl principal = (LdapUserDetailsImpl) authentication.getPrincipal();
log.info("authentication: " + authentication);
log.info("principal: " + principal);
return "Spring Security + Spring LDAP Authentication Configuration Example";
}
@GetMapping("/managers")
public String managers(){
return "Hello managers";
}
@GetMapping("/employees")
public String employees(){
return "Hello employees";
}
}
Bootstrap Spring Application
We bootstrap the application using Spring Boot. In the example we used Spring Java Configuration. You can also use Spring XML configuration. If you need Spring XML Configuration, you can enable it by using the @ImportResource("classpath:spring-security-config.xml")
.
package com.memorynotfound.ldap;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Configuration;
@Configuration
@SpringBootApplication
// Enable if you want to use Spring Security + LDAP Authentication XML Configuration
// @ImportResource("classpath:spring-security-config.xml")
public class Application {
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
}
Spring Security + Spring LDAP Authentication Example
After the application is initialized go to the http://localhost:8080
url and you’ll see the following screen. You can login using the username john
with password secret
.
After a succesfull login we see the following screen.
The user uid=john,ou=people
resides in the group cn=managers,ou=groups
which results in having a role ROLE_MANAGERS
so he can access the managers rest service.
The user uid=john,ou=people
does not in the group cn=employees,ou=groups
which means he cannot access the employees rest service
Spring Security + Spring LDAP Authentication Integration Tests
Now we created a successful Spring Security LDAP authentication application, we can write some integration tests to verify everything keeps working.
The @AutoConfigureMockMvc
annotation auto configures the MockMvc
.
Using the MockMvc
class we can perform invocations on server side endpoints. In the following integration tests we first create a FormLoginRequestBuilder
using SecurityMockMvcRequestBuilders.formLogin()
method and pass the credentials. Next,
we perform a MockMvc.perform()
invocation and validate the result using the SecurityMockMvcResultMatchers.authenticated()
or SecurityMockMvcResultMatchers.unauthenticated()
result matchers, based on the expected outcome.
package com.memorynotfound.ldap;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.FormLoginRequestBuilder;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.unauthenticated;
@SpringBootTest
@AutoConfigureMockMvc
@ActiveProfiles("test")
@RunWith(SpringRunner.class)
public class ApplicationTests {
@Autowired
private MockMvc mockMvc;
@Test
public void loginWithValidUserThenAuthenticated() throws Exception {
FormLoginRequestBuilder login = formLogin().user("john").password("secret");
mockMvc.perform(login).andExpect(authenticated().withUsername("john"));
}
@Test
public void loginWithInvalidUserThenUnauthenticated() throws Exception {
FormLoginRequestBuilder login = formLogin().user("invalid").password("invalidpassword");
mockMvc.perform(login).andExpect(unauthenticated());
}
}
Spring Security + Spring LDAP Authentication Integration Test Results
References
- Spring Security Ldap Authentication Documentation
- Spring Security API
- @EnableWebSecurity JavaDoc
- WebSecurityConfigurerAdapter JavaDoc
- MockMvc JavaDoc
tks so much….
thaks for all
Is this possible to use external ldap server by configuration from application.properties as spring.ldap.urls, spring.ldap.username, spring.ldap.password and use embedded ldap server for intgration tests by configuration from application-test.properties as spring.ldap.embedded.* ? If it is possible, could anyone show example of that.
Yes it’s possible, but I don’t have any example of that at the moment.
I am unable to change password. I need to set “spring” – Plain text and SHA1 is “{SHA}e9ci7ye4IPJJc4z1HtAJVYhwE7U=”. can you help me to change the password
thanks a lot !