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
Download
From:一号门
COMMENTS