Spring Security LDAP Authentication Example

By Arvind Rai, 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 
Here on this page we will create Spring Boot Security LDAP authentication application using bcrypt password encoding strategy.

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 resolved unboundid-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 
The ldif file location, base DN and port has been configured in 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 call ldapAuthentication() method of AuthenticationManagerBuilder class. The method ldapAuthentication() returns the below class.
LdapAuthenticationProviderConfigurer 
Above class provides methods to configure LDAP. Find some of its methods.
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");		
} 
The 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;
	}
} 
Find the equivalent XML configuration file.
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.
Spring Security LDAP Authentication Example
The files 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> 
info.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> 
access-denied.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> 
HomeController.java
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;
	}
} 
Main.java
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 
Access the URL.
http://localhost:8080/secure/dev 
Enter credentials surya/s123
Spring Security LDAP Authentication Example
We will see welcome page as following.
Spring Security LDAP Authentication Example

References

Spring Security Reference
Authenticating a User with LDAP
Lightweight Directory Access Protocol

Download Source Code

POSTED BY
ARVIND RAI
ARVIND RAI







©2024 concretepage.com | Privacy Policy | Contact Us