Spring Security LDAP Authentication Example
December 25, 2019
This page will walk through Spring Security LDAP authentication example. LDAP is Lightweight Directory Access Protocol that is used to interact with directory server. LDAP is used for authentication and storing information about users, groups and applications. Spring Security provides LdapAuthenticationProvider
class to authenticate a user against a LDAP server. The equivalent XML element is <ldap-authentication-provider>
.
LDAP node is created with following keywords.
o : Organization
ou : Organizational unit
cn : Common Name
sn : Surname
uid : User Id
dn : Distinguished name
dc : Domain Component
When the user submits login form, then to find the user a LDAP DN is created. Suppose the username is 'krishna' then the actual name used to authenticate to LDAP will be the full DN as following.
uid=krishna,ou=people,dc=concretepage,dc=com
Contents
Technologies Used
Find the technologies being used in our example.1. Java 11
2. Spring 5.2.1.RELEASE
3. Spring Boot 2.2.1.RELEASE
4. UnboundID LDAP SDK 4.0.12
5. Maven 3.5.2
Maven Dependencies
Find the Maven dependencies used in our example.pom.xml
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.1.RELEASE</version> <relativePath /> </parent> <properties> <context.path>spring-app</context.path> <java.version>11</java.version> </properties> <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> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> </dependencies>
LDAP Server
For the Spring Security LDAP authentication demo, we are using UnboundID LDAP SDK as embedded server in our application. For UnboundID LDAP SDK, we have resolvedunboundid-ldapsdk
dependency in our pom.xml
file.
For the demo, we have created a ldif file in our classpath. It has two roles
managers
and developers
. We have created two users, one is krishna
with password k123
with roles managers
and developers
. Another user is surya
with password s123
with role developers
. The password is in bcrypt encoding. Find the ldif file.
my-server.ldif
dn: dc=concretepage,dc=com objectclass: top objectclass: domain objectclass: extensibleObject dc: concretepage dn: ou=groups,dc=concretepage,dc=com objectclass: top objectclass: organizationalUnit ou: groups dn: ou=people,dc=concretepage,dc=com objectclass: top objectclass: organizationalUnit ou: people dn: uid=krishna,ou=people,dc=concretepage,dc=com objectclass: top objectclass: person objectclass: organizationalPerson objectclass: inetOrgPerson cn: Krishna Sharma sn: Sharma uid: krishna userPassword: $2a$10$nnu2.EBSnJUQZmOv5hbD8.3C8dlifeLi26AWpoKN31FqjNXrijQMq dn: uid=surya,ou=people,dc=concretepage,dc=com objectclass: top objectclass: person objectclass: organizationalPerson objectclass: inetOrgPerson cn: Surya Singh sn: Singh uid: surya userPassword: $2a$10$DPzHnInANVY1utbuRfe0eOojtE02k23TGB5Q0L6mIHOBJQhKU7DTi dn: cn=developers,ou=groups,dc=concretepage,dc=com objectclass: top objectclass: groupOfUniqueNames cn: developers ou: developer uniqueMember: uid=krishna,ou=people,dc=concretepage,dc=com uniqueMember: uid=surya,ou=people,dc=concretepage,dc=com dn: cn=managers,ou=groups,dc=concretepage,dc=com objectclass: top objectclass: groupOfUniqueNames cn: managers ou: manager uniqueMember: uid=krishna,ou=people,dc=concretepage,dc=com
application.properties
file in our Spring Boot application.
application.properties
spring.ldap.embedded.ldif=classpath:my-server.ldif spring.ldap.embedded.base-dn=dc=concretepage,dc=com spring.ldap.embedded.port=2389
Spring Security LDAP Java Configuration
To configure LDAP authentication provider using Java Configuration we need to callldapAuthentication()
method of AuthenticationManagerBuilder
class. The method ldapAuthentication()
returns the below class.
LdapAuthenticationProviderConfigurer
userDnPatterns(): Pass LDAP patterns as argument to find username.
userSearchBase(): Pass search base as argument for user searches.
userSearchFilter(): Pass LDAP filter as argument for user searches.
groupSearchBase(): Pass search base as argument for group membership searches.
groupSearchFilter(): Pass LDAP filter as argument to search for groups.
contextSource(): Configures base LDAP path context source.
passwordCompare(): Configures LDAP compare operation of the user password to authenticate.
Find the code snippet to configure LDAP with Spring Security.
@Override protected 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() .url("ldap://localhost:2389/dc=concretepage,dc=com") .and() .passwordCompare() .passwordEncoder(passwordEncoder()) .passwordAttribute("userPassword"); }
contextSource()
method returns ContextSourceBuilder
class that is used to build base LDAP path context source. Find some of its methods.
ldif(): Loads a given ldif file at startup for an embedded LDAP server.
managerDn(): Pass username as DN of the "manager" to authenticate non-embedded LDAP server.
managerPassword(): Password for the manager DN.
port(): Port to connect to LDAP.
root(): Optional root suffix for the embedded LDAP server.
url(): Specify LDAP server URL.
The
passwordCompare()
method returns PasswordCompareConfigurer
that sets up password based comparison. It has following methods.
passwordAttribute(): Specifies the attribute in the LDAP which contains the user password. Default attribute name is
userPassword
.
passwordEncoder(): Specifies password encoder. Default is
NoOpPasswordEncoder
.
Find the compete file of Spring Security LDAP Java configuration.
SecurityConfig.java
package com.concretepage; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; 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.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/secure/man/**").access("hasRole('MANAGERS')") .antMatchers("/secure/dev/**").access("hasRole('DEVELOPERS')") .and().formLogin() .loginPage("/login") .loginProcessingUrl("/appLogin") .usernameParameter("username") .passwordParameter("password") .defaultSuccessUrl("/secure/dev") .and().logout() .logoutUrl("/appLogout") .logoutSuccessUrl("/login") .and().exceptionHandling() .accessDeniedPage("/accessDenied"); } @Override protected 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() .url("ldap://localhost:2389/dc=concretepage,dc=com") .and() .passwordCompare() .passwordEncoder(passwordEncoder()) .passwordAttribute("userPassword"); } @Bean public PasswordEncoder passwordEncoder() { BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); return passwordEncoder; } }
security-config.xml
<?xml version="1.0" encoding="UTF-8"?> <beans:beans xmlns="http://www.springframework.org/schema/security" xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd"> <http> <intercept-url pattern="/secure/man/**" access="hasRole('MANAGERS')" /> <intercept-url pattern="/secure/dev/**" access="hasRole('DEVELOPERS')" /> <form-login login-page="/login" login-processing-url="/appLogin" username-parameter="username" password-parameter="password" default-target-url="/secure/dev"/> <logout logout-url="/appLogout" logout-success-url="/login"/> <access-denied-handler error-page="/accessDenied"/> </http> <beans:bean name="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/> <authentication-manager> <ldap-authentication-provider server-ref="ldapServer" 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 password-attribute="userPassword"> <password-encoder ref="passwordEncoder"/> </password-compare> </ldap-authentication-provider> </authentication-manager> <ldap-server id="ldapServer" url="ldap://localhost:2389/dc=concretepage,dc=com" /> </beans:beans>
Complete Example
Find the project structure of our demo application.my-server.ldif
, application.properties
, pom.xml
and SecurityConfig.java
have been already given above in the article. Here find the other files of our demo application.
custom-login.html
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"> <head> <title>Spring Security Example</title> <link rel="stylesheet" href="/css/styles.css"/> </head> <body> <h3>Spring Security Example</h3> <p th:if="${param.error}" class="error"> Bad Credentials </p> <form th:action="@{/appLogin}" method="POST"> <table border='1' cellspacing='0' cellpadding='10'> <tr><td>Username:</td> <td> <input type="text" name="username"/> </td></tr> <tr><td>Password:</td> <td> <input type="password" name="password"/> </td></tr> <tr><td colspan='2' align='center'> <input type="submit" value="Login"/> </td> </tr> </table> </form> </body> </html>
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"> <head> <title>Spring Security Example</title> <link rel="stylesheet" href="/css/styles.css"/> </head> <body> <h3>Spring Security Example</h3> Welcome to <b th:inline="text" class="user"> [[${#httpServletRequest.remoteUser}]] </b> <br/><br/> <form th:action="@{/appLogout}" method="POST"> <input type="submit" value="Logout"/> </form> </body> </html>
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"> <head> <title>Spring Security Example</title> <link rel="stylesheet" href="/css/styles.css"/> </head> <body> <h3>Access Denied Exception</h3> Logged in user: <b th:inline="text" class="user"> [[${#httpServletRequest.remoteUser}]] </b> <br/><br/> <form th:action="@{/appLogout}" method="POST"> <input type="submit" value="Logout"/> </form> <p class="error" th:text="${errorMsg}">Error</p> </body> </html>
package com.concretepage; import org.springframework.security.core.Authentication; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.servlet.ModelAndView; @Controller public class HomeController { @GetMapping("/login") public ModelAndView login() { ModelAndView mav = new ModelAndView(); mav.setViewName("custom-login"); return mav; } @GetMapping("/secure/man") public ModelAndView welcome1(Authentication authentication) { ModelAndView mav = new ModelAndView(); mav.setViewName("info"); return mav; } @GetMapping("/secure/dev") public ModelAndView welcome2(Authentication authentication) { ModelAndView mav = new ModelAndView(); mav.setViewName("info"); return mav; } @GetMapping("/accessDenied") public ModelAndView error() { ModelAndView mav = new ModelAndView(); String errorMessage = "You are not authorized to access this page."; mav.addObject("errorMsg", errorMessage); mav.setViewName("access-denied"); return mav; } }
package com.concretepage; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Main { public static void main(String[] args) { SpringApplication.run(Main.class, args); } }
Output
Download the project and run the following command from root folder of the project using command prompt.mvn spring-boot:run
http://localhost:8080/secure/dev
References
Spring Security ReferenceAuthenticating a User with LDAP
Lightweight Directory Access Protocol