You are here
Home > java >

Spring Security LDAP Authentication Example

Spring Security LDAP Authentication ExampleIf you have ever implemented login feature in a production grade application, you must have heard about the LDAP authentication. LDAP can be used in any type of the hierarchical directory information. The most popular use of LDAP is to store an organizational data. A typical organization generally has directors, managers, supervisors and other positions. In other words, these are the hierarchical data where LDAP mechanism is perfect to implement. In this way, most of the organizations use it to maintain the organizational information including their credentials. We will discuss Spring Security LDAP Authentication Example in this article.

In Spring based applications, LDAP is used to integrate with LDAP directories and perform various directory-related operations, such as user authentication and authorization. Spring provides tools and libraries to work with LDAP smoothly, making it a popular choice for developing applications that require LDAP integration.

What is LDAP?

LDAP stands for Lightweight Directory Access Protocol. It is an open, vendor-neutral, industry standard application protocol to access and maintain distributed directory information services over the network.

How does LDAP work?

As aforementioned, LDAP stores the userโ€™s information of an organization. When a user tries to login to an application, it checks with LDAP to see if it is a valid user and if that user has required rights & valid credentials. In the context of Spring Security, the application connects to the LDAP server in order to verify the valid user against that LDAP.

LDAP integration in Spring is valuable for organizations that already use LDAP directories for user management and want to extend this infrastructure to secure and manage their Spring based applications. It simplifies user management and enhances the security and authorization capabilities of Spring based applications.

What is LDIF?

LDIF is an abbreviation for LDAP Data Interchange Format. It is a standard plain text data interchange format of a file that is used for representing LDAP directory content. The extension of the file is โ€˜.ldifโ€™. There are multiple fields in the LDIF file. Letโ€™s know about them:

What are the common fields in an LDIF file?

1) dn : distinguished name

โ€˜dnโ€™ represents the name that uniquely identifies an entry in the directory. DN is a mandatory field. If there is a comma in the DN, the comma must be escaped with a backslash (\). For example, dn: uid=sam, ou=people, o=example.com Bolivia\,S.A.

2) o : organization

โ€˜oโ€™ represents the organization name. For example: google.com

3) dc : domain component

โ€˜dcโ€™ represents each component of the domain. For example www.example.com would be written as DC=www, DC=example, DC=com

4) ou : organizational unit

โ€˜ouโ€™ represents the organizational unit (or sometimes the user group) that the user is part of. If the user is part of more than one group, you may specify it by providing multiple entries. For example:ย  OU= employee, OU= admin.

5) cn : common name

โ€˜cnโ€™ represents the individual object (personโ€™s name; meeting room; recipe name; job title; etc.) for whom/which you are querying. It is commonly used by the person. For example,ย cn: Bill Anderson. At least one common name is required.

6) sn : surname

โ€˜snโ€™ represents the personโ€™s surname, or last name. For example,ย sn: Anderson. A surname is required.

7) objectClass : object class

โ€˜objectClassโ€™ specifies an object class to use with this entry.. It is a mandatory field. For example: objectClass: person, objectClass: organizationalPerson. This object class specification should be included because many LDAP clients require it during search operations for a person or an organizational person. You may refer oracle doc Object Class Referenceย for a list of standard object classes.

What is the format of an LDIF file?

LDIF consists of one or more directory entries separated by a blank line. Each LDIF entry consists of an optional entry ID, a required distinguished name, one or more object classes, and multiple attribute definitions.ย The LDIF format is defined in RFC 2849ย The LDAP Data Interchange Format (LDIF). Sun Java System Directory Server is compliant with this standard.

The basic form of a directory entry represented in LDIF is as follows:

dn:ย distinguished_name
objectClass:ย object_class
objectClass:ย object_class
...
attribute_type[;subtype]:attribute_value
attribute_type[;subtype]:attribute_value
...

We must supply the DN and at least one object class definition. In addition, we must include any attributes required by the object classes that we define for the entry. All other attributes and object classes are optional. We can specify object classes and attributes in any order. The space after the colon is also optional. For example, below is a valid entry in an LDIF file. Here field โ€˜uidโ€™ stands for user id of the person.

dn: uid=bob,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
cn: Bob Hamilton
sn: Hamilton
uid: bob
userPassword: bobspassword

How to implement Spring Security LDAP Authentication Example Using Spring Boot?

Spring Security, a widely used security framework in Spring Boot, provides LDAP support for user authentication and authorization, making it clear-cut to secure our application using LDAP.

Use-case Details

Letโ€™s assume that we have an application where we have already implemented basic security using the Spring Security API provide by Spring Framework. Now we have to implement LDAP authentication in order to make userโ€™s interaction more secure. Therefore, we need to develop a basic web application and then implement LDAP authentication concepts to fulfil the requirements of Spring Security LDAP Authentication Example.

Letโ€™s develop ย Spring Security LDAP Authentication Exampleย Using Spring Boot step by step as below:

Step#1: Create a Spring Boot Starter Project using STS

Here, we are using STS (Spring tool Suite) as an IDE to develop the example. While creating Starter Project select โ€˜Spring Webโ€™, โ€˜Spring LDAPโ€™, โ€˜Embedded LDAP Serverโ€™, โ€˜Spring securityโ€™ and โ€˜Spring Boot DevToolsโ€™ as starter project dependencies. Here โ€˜Spring Boot Dev Toolsโ€™ is optional to add. We are using Spring Boot Dev Tools to avoid restarting tomcat server multiple times while testing the application. Apart from that, we need to add one additional dependency of โ€˜spring-security-ldapโ€™.ย 

Even If you donโ€™t know how to create Spring Boot Starter Project, Kindly visit a separate articleย on How to create a starter project Using Spring Boot?.

Below are the dependencies that we have used to develop this Spring Security LDAP Authentication Example Using Spring Boot.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-ldap</artifactId>
</dependency>

<dependency>
    <groupId>com.unboundid</groupId>
    <artifactId>unboundid-ldapsdk</artifactId>
<!-- <scope>test</scope> -->
</dependency>

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-ldap</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <scope>runtime</scope>
</dependency>

Step#2: Create a .ldif file as ldap-data.ldif

This file will have a mandatory โ€˜.ldifโ€™ extension. Create a file named โ€˜ldap-data.ldifโ€™ under the folder src/main/resources and update the data as shown below. We have taken this file from Spring Official documentation website. It will work as local directory and have compatible data to work with LDAP accordingly.

dn: dc=springframework,dc=org
objectclass: top
objectclass: domain
objectclass: extensibleObject
dc: springframework

dn: ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: groups

dn: ou=subgroups,ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: subgroups

dn: ou=people,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: people

dn: ou=space cadets,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: space cadets

dn: ou=\"quoted people\",dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: "quoted people"

dn: ou=otherpeople,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: otherpeople

dn: uid=ben,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Ben Alex
sn: Alex
uid: ben
userPassword: $2a$10$c6bSeWPhg06xB1lvmaWNNe4NROmZiSpYhlocU/98HNr2MhIOiSt36

dn: uid=admin,ou=people,dc=springframework,dc=org 
objectclass: top 
objectclass: person 
objectclass: organizationalPerson 
objectclass: inetOrgPerson 
cn: Admin 
sn: Admin 
uid: admin 
userPassword: $2a$10$ODZhWAyD0i8zruWQcMYx9ePkW2xXsqIljhh4K8spSOUCY897ERkwu

dn: uid=bob,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Bob Hamilton
sn: Hamilton
uid: bob
userPassword: bobspassword

dn: uid=joe,ou=otherpeople,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Joe Smeth
sn: Smeth
uid: joe
userPassword: joespassword

dn: cn=mouse\, jerry,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Mouse, Jerry
sn: Mouse
uid: jerry
userPassword: jerryspassword

dn: cn=slash/guy,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: slash/guy
sn: Slash
uid: slashguy
userPassword: slashguyspassword

dn: cn=quote\"guy,ou=\"quoted people\",dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: quote\"guy
sn: Quote
uid: quoteguy
userPassword: quoteguyspassword

dn: uid=space cadet,ou=space cadets,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Space Cadet
sn: Cadet
uid: space cadet
userPassword: spacecadetspassword

dn: cn=developers,ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: groupOfUniqueNames
cn: developers
ou: developer
uniqueMember: uid=ben,ou=people,dc=springframework,dc=org
uniqueMember: uid=bob,ou=people,dc=springframework,dc=org

dn: cn=managers,ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: groupOfUniqueNames
cn: managers
ou: manager
uniqueMember: uid=ben,ou=people,dc=springframework,dc=org
uniqueMember: cn=mouse\, jerry,ou=people,dc=springframework,dc=org

dn: cn=submanagers,ou=subgroups,ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: groupOfUniqueNames
cn: submanagers
ou: submanager
uniqueMember: uid=ben,ou=people,dc=springframework,dc=org

Here, we have highlighted one userโ€™s data that we will use to test the implemented example.

Step#3: Update application.properties file

In this file we will have some properties of our embedded LDAP as shown below.

spring.ldap.embedded.port=8389
spring.ldap.embedded.ldif=classpath:ldap-data.ldif
spring.ldap.embedded.base-dn=dc=springframework,dc=org

Step#4: Create a Controller class for basic authentication

Next step is to create a RestController class. When we enter the credentials in order to login to the application, we will see a successful login message. Letโ€™s create a simple controller LoginController.java as below.

LoginController.java

package com.dev.springboot.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class LoginController {

   @GetMapping("/")
   public String getLoginPage() {
       return "You have successfully logged in Using Spring Security LDAP Authentication!";
   }
}

Step#5A: Create a Configuration class as LdapSecurityConfig.java

This is the configuration class where we will keep the whole logic of LDAP Authentication as shown below.

LdapSecurityConfig.java

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.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@Configuration
public class LdapSecurityConfig extends WebSecurityConfigurerAdapter {

   @Override
   protected void configure(HttpSecurity http) throws Exception {
       http
        .authorizeRequests()
        .anyRequest().fullyAuthenticated()
        .and()
        .formLogin();
   }

   @Override
   public void configure(AuthenticationManagerBuilder auth) throws Exception {
       auth
        .ldapAuthentication()
        .userDnPatterns("uid={0},ou=people")
        .groupSearchBase("ou=groups")
        .contextSource()
        .url("ldap://localhost:8389/dc=springframework,dc=org")
        .and()
        .passwordCompare()
        .passwordEncoder(new BCryptPasswordEncoder())
        .passwordAttribute("userPassword");
   }
}

Note: If you are working with Spring Security 5.7.0-M2 or later, you are not supposed to use WebSecurityConfigurerAdapter. In that case, kindly visit a separate article on โ€˜Spring Security Without WebSecurityConfigurerAdapterโ€˜.

You can also take help of the below code in Step#5B for reference. The implementation is done without using WebSecurityConfigurerAdapter.

Step#5B: Create a Configuration class as LdapSecurityConfig.java Without WebSecurityConfigurerAdapter

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import org.springframework.security.ldap.authentication.BindAuthenticator;
import org.springframework.security.ldap.authentication.LdapAuthenticationProvider;
import org.springframework.security.ldap.search.FilterBasedLdapUserSearch;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
public class LdapSecurityConfig {

    protected SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

      http
       .authorizeHttpRequests()
       .anyRequest().fullyAuthenticated()
       .and()
       .formLogin();
      http.authenticationProvider(ldapAuthenticationProvider());
      return http.build();
    }

    @Bean
    LdapAuthenticationProvider ldapAuthenticationProvider() {
       return new LdapAuthenticationProvider(authenticator());
    }

    @Bean
    BindAuthenticator authenticator() {

       FilterBasedLdapUserSearch search = new FilterBasedLdapUserSearch("ou=groups", "(uid={0})", contextSource());
       BindAuthenticator authenticator = new BindAuthenticator(contextSource());
       authenticator.setUserSearch(search);
       return authenticator;
    }

    @Bean
    public DefaultSpringSecurityContextSource contextSource() {
       DefaultSpringSecurityContextSource dsCtx = new DefaultSpringSecurityContextSource("ldap://localhost:8389/dc=springframework,dc=org");
       dsCtx.setUserDn("uid={0},ou=people");
       return dsCtx;
    }
}

How to test the Application?

In order to test the application, we need to open a browser and hit the URL http://localhost:8080/. On hitting the URL, the default login page will get displayed. Further, enter any credentials given in the ldap-data.ldif file. If inserted credentials are correct, you will see the successful login message using LDAP Authentication. For example, letโ€™s observe the below userโ€™s data in the ldap-data.ldif file.

dn: uid=admin,ou=people,dc=springframework,dc=org 
objectclass: top 
objectclass: person
objectclass: organizationalPerson 
objectclass: inetOrgPerson 
cn: Admin 
sn: Admin 
uid: admin 
userPassword: $2a$10$ODZhWAyD0i8zruWQcMYx9ePkW2xXsqIljhh4K8spSOUCY897ERkwu

Here, aid is the username and userpassword is the encrypted value of password. The real value of password is โ€˜admin@123โ€™. Hence, we need to provide admin@123 as a password. Once you click on Sign In button, you should see the message โ€˜You have successfully logged in Using Spring Security LDAP Authentication!โ€™ on the screen.

We can use below Utility class to encode any password.

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

public class PasswordEncoder {

    public static void main(String[] args) {
       System.out.println(new BCryptPasswordEncoder().encode("admin@123"));
    }
}

FAQ

Are there any alternatives to LDAP for user management in Spring Boot?

Yes, Spring Boot offers support for various authentication and user management mechanisms, including database-backed authentication, OAuth2, and more. The choice depends on your applicationโ€™s requirements and existing infrastructure.

Can we use LDAP in combination with other authentication mechanisms in Spring Boot?

Yes, we can combine LDAP authentication with other authentication mechanisms, such as OAuth2, in Spring Boot applications. This allows us to provide multiple authentication options to users.

Conclusion

In this article we have covered all the theoretical and example part of โ€˜Spring Security LDAP Authentication Example Using Spring Bootโ€™, finally, you should be able to implement Spring Security LDAP Authentication. Similarly, we expect from you to further extend this example, as per your requirement. Also try to implement it in your project accordingly. Moreover, Feel free to provide your comments in the comments section below.

Leave a Reply


Top