SpringBoot integrates Ldap–super detailed method explanation

LADP Overview

LDAP (Lightweight Directory Access Protocol) is a protocol for accessing and maintaining distributed directory information services. Directory service is a service for storing and retrieving information. It is usually used to store user information, organizational structure, network equipment and other data within an organization. LDAP is a lightweight protocol designed for lookup and modification operations in directories rather than for transmitting large amounts of data.

The following are some basic concepts of LDAP:

Directory Service: Directory Service is a service specifically designed for storing and retrieving information. Unlike traditional databases, directory services focus more on providing efficient read operations and supporting fast data retrieval. LDAP is a protocol used to communicate with directory services.

Directory: Directory is a way of organizing structured data, usually containing multiple entries. Each entry contains a set of attribute values that describe information about a specific entity (such as a user, organizational unit, device, etc.).

Entry: An entry is the basic unit in a directory, similar to a row of records in a database. Each entry has a unique identifier called DN (Distinguished Name).

Attribute: An attribute is a name and value pair of information stored in an entry. For example, a user entry might contain attributes such as name, email address, phone number, etc.

DN (Distinguished Name): DN is the unique identifier of each entry in the directory and consists of a series of names related to the directory structure. DN is usually a hierarchical structure, such as “cn=john,ou=users,dc=example,dc=com”.

LDAP Protocol: LDAP defines the rules for communication between clients and directory servers. It uses the TCP/IP protocol stack and usually runs on port 389. The LDAP protocol supports a variety of operations, including search, add, delete, modify, etc., to operate on data in the directory.

LDAP is widely used in enterprises and organizations to centrally manage information such as users, organizational structure, equipment, etc. It is the basis for many authentication and access control systems, such as single sign-on (SSO) systems.

Step 1: Configure LDAP connection properties:

Add LDAP connection properties, such as LDAP server URL, username, password, etc., in the application.properties or application.yml file.

Step 2: Create LDAP configuration class:

Create a @Configuration class and enable web security configuration using the @EnableWebSecurity annotation. In the configuration class, you can use the relevant configuration properties of LDAP to configure the LDAP connection and authentication provider.

Step 3: Create an LDAP authentication provider:

Implement the UserDetailsService interface and rewrite the loadUserByUsername() method in it. To integrate LDAP (Lightweight Directory Access Protocol) in the Spring Boot project, you can use the Spring LDAP module. The following is a simple example showing how to integrate LDAP in Spring Boot and provides some common operation examples.

Integration steps

1.Introduce POM dependencies

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

2. Provide the configuration information of the LDAP connection in the application.properties or application.yml file

ldap.url=ldap://your-ldap-server:389
ldap.base=dc=example,dc=com
ldap.userDnPattern=uid={<!-- -->0},ou=people
ldap.userSearchBase=ou=people

3. Create LdapDemoConfig

package com.example.springbootidapdemo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.core.support.LdapContextSource;

@Configuration
public class LdapDemoConfig {<!-- -->
    @Bean
    public LdapTemplate ldapTemplate() {<!-- -->
        LdapContextSource contextSource = new LdapContextSource();
        contextSource.setUrl("ldap://localhost:389");
        contextSource.setBase("dc=example,dc=com");
        contextSource.setUserDn("cn=admin,dc=example,dc=com");
        contextSource.setPassword("adminpassword");
        contextSource.afterPropertiesSet();

        LdapTemplate ldapTemplate = new LdapTemplate(contextSource);
        ldapTemplate.setIgnorePartialResultException(true);
        ldapTemplate.setDefaultTimeLimit(1000);
        ldapTemplate.setDefaultCountLimit(100);

        return ldapTemplate;
    }
}

4. Method overview and corresponding Demo

Prerequisite: Basic DN, filter, search control and attribute mapper need to be provided. In actual use, it needs to be configured accordingly according to the structure and attributes of the LDAP directory.

1.ldapTemplate.find (find entries in the LDAP directory based on specified search criteria and return results)

public <T> T find(
        String dn,
        String filter,
        SearchControls controls,
        AttributesMapper<T> mapper)

dn: The base DN (Distinguished Name) of the search, indicating the starting position of the search.
filter: LDAP search filter, defining the conditions for entries to be matched.
controls: Search controls, used to configure some parameters of the search, such as search range, returned properties, etc.
mapper: AttributesMapper for mapping search results to objects.

This method performs an LDAP search and returns the first entry that meets the criteria. If there is no matching entry, null is returned.

The demo is as follows:

@Service
public class LdapService {<!-- -->

    @Autowired
    private LdapTemplate ldapTemplate;

    public User findUser(String username) {<!-- -->
        String baseDn = "ou=people,dc=example,dc=com";
        String filter = "(uid=" + username + ")";
        
        SearchControls controls = new SearchControls();
        controls.setSearchScope(SearchControls.SUBTREE_SCOPE);

        return ldapTemplate.find(baseDn, filter, controls, new UserAttributesMapper());
    }

    private static class UserAttributesMapper implements AttributesMapper<User> {<!-- -->
        @Override
        public User mapFromAttributes(Attributes attributes) throws NamingException {<!-- -->
            return new User(/* user properties */);
        }
    }
}

The following demo only shows key code blocks and repeated descriptions will not be elaborated in detail.

2.ldapTemplate.findAll (find multiple entries in the LDAP directory based on specified search criteria and return a list of results)

public <T> List<T> findAll(
        String base,
        String filter,
        SearchControls controls,
        AttributesMapper<T> mapper)

base: The base DN (Distinguished Name) of the search, indicating the starting position of the search.

This method performs an LDAP search and returns a list of all entries that meet the criteria. If there are no matching entries, an empty list is returned.

The demo is as follows:

public List<User> findAllUsers() {<!-- -->
        String baseDn = "ou=people,dc=example,dc=com";
        String filter = "(objectClass=person)";
        SearchControls controls = new SearchControls();
        controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
        return ldapTemplate.findAll(baseDn, filter, controls, new UserAttributesMapper());
    }

3.ldapTemplate.findOne (finds a single entry in the LDAP directory based on specified search criteria and returns the result)

Note: If there is no matching entry, a javax.naming.NameNotFoundException exception is thrown. If there are multiple matching entries, throw javax.naming.NamingException exception

public <T> T findOne(
        String dn,
        String filter,
        AttributesMapper<T> mapper)

The demo is as follows:

public User findUser(String username) {<!-- -->
        String baseDn = "ou=people,dc=example,dc=com";
        String filter = "(uid=" + username + ")";
        return ldapTemplate.findOne(baseDn, filter, new UserAttributesMapper());
    }

4.ldapTemplate.findByDn (find a single entry based on the given DN (Distinguished Name) and return the result)

public <T> T findByDn(String dn, AttributesMapper<T> mapper)

This method performs an LDAP search and returns the entry for the specified DN, or null if no matching entry is found.

The demo is as follows:

 public User findUserByDn(String userDn) {<!-- -->
        return ldapTemplate.findByDn(userDn, new UserAttributesMapper());
    }

5.ldapTemplate.findForStream (used to stream the results of multiple entries from an LDAP directory)

public <T> Stream<T> findForStream(
        String base,
        String filter,
        SearchControls controls,
        AttributesMapper<T> mapper)

The ldapTemplate.findForStream method performs an LDAP search and returns a Stream containing the results for all entries that meet the criteria. By using streams, you can process large numbers of search results more efficiently without having to load them all into memory at once.

The demo is as follows:

public List<User> findAllUsers() {<!-- -->
        String baseDn = "ou=people,dc=example,dc=com";
        String filter = "(objectClass=person)";
        SearchControls controls = new SearchControls();
        controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
        Stream<User> userStream = ldapTemplate.findForStream(baseDn, filter, controls, new UserAttributesMapper());
        return userStream.collect(Collectors.toList());
    }

Note: The ldapTemplate.findForStream method was introduced starting with Spring LDAP version 2.2. Make sure you are using a compatible Spring LDAP version. If the version is older, you may need to upgrade to a compatible version or use another method to handle the LDAP search results flow

6.ldapTemplate.search (used to perform flexible LDAP searches and return results)

public <T> List<T> search(
        String base,
        String filter,
        SearchControls controls,
        AttributesMapper<T> mapper)

Similar to the ldapTemplate.findAll method introduced earlier, the ldapTemplate.search method also performs an LDAP search, but it provides more flexibility to configure search parameters more precisely. This method returns a list of all entries that satisfy the condition.

The demo is as follows:

public List<User> searchUsers(String filter) {<!-- -->
        String baseDn = "ou=people,dc=example,dc=com";
        SearchControls controls = new SearchControls();
        controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
        return ldapTemplate.search(baseDn, filter, controls, new UserAttributesMapper());
    }

7.ldapTemplate.searchForContext (Context used to perform LDAP searches and return search results (DirContext))

public DirContext searchForContext(
        String base,
        String filter,
        SearchControls controls)

Compared with the ldapTemplate.search method introduced before, ldapTemplate.searchForContext returns the context of the search results instead of directly returning the mapped object list. Such a design allows for more flexible processing of search results, including further processing, parsing, and manipulation of search results.

The demo is as follows:

public DirContext searchForContext(String filter) {<!-- -->
        String baseDn = "ou=people,dc=example,dc=com";
        SearchControls controls = new SearchControls();
        controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
        return ldapTemplate.searchForContext(baseDn, filter, controls);
    }

8.ldapTemplate.searchForObject (used to perform an LDAP search and return a single object as a result)

public <T> T searchForObject(
        String base,
        String filter,
        SearchControls controls,
        Class<T> requiredType)

requiredType: expected return type.

This method performs an LDAP search and returns a single object rather than a list of objects. This can be conveniently used to find unique entries under certain conditions.

The demo is as follows:

public User searchForUser(String username) {<!-- -->
        String baseDn = "ou=people,dc=example,dc=com";
        String filter = "(uid=" + username + ")";
        SearchControls controls = new SearchControls();
        controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
        return ldapTemplate.searchForObject(baseDn, filter, controls, User.class);
    }

Note: The ldapTemplate.searchForObject method will return null if no matching entry is found

9.ldapTemplate.searchForStream (Stream used to perform LDAP searches and return results)

public <T> Stream<T> searchForStream(
        String base,
        String filter,
        SearchControls controls,
        Class<T> requiredType)

This method performs an LDAP search and returns a Stream containing the results for all entries that meet the criteria. By using streams, you can process large numbers of search results more efficiently without having to load them all into memory at once.

The demo is as follows:

public List<User> searchForUsers(String filter) {<!-- -->
        String baseDn = "ou=people,dc=example,dc=com";
        SearchControls controls = new SearchControls();
        controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
        Stream<User> userStream = ldapTemplate.searchForStream(baseDn, filter, controls, User.class);
        return userStream.collect(Collectors.toList());
    }

10.ldapTemplate.rebind (used to rebind (update) entries in the LDAP directory)

public void rebind(String dn, Object obj, Attributes attributes)

obj: The object to be rebound.

This method will rebind the specified objects and attributes to the specified DN in the LDAP directory. If the specified DN does not exist, a new entry will be created. If an entry with the same DN already exists, the properties of the existing entry are updated.

 public void updateUserInfo(String userDn, User newUser) {<!-- -->
        // Assume User class has appropriate getters and setters for user attributes
        Attributes attributes = // create or update attributes based on newUser

        ldapTemplate.rebind(userDn, newUser, attributes);
    }

Note: The creation or update of specific Attributes objects needs to be adjusted based on your data model and needs

11.ldapTemplate.rename (used to rename (move) entries in the LDAP directory)

public void rename(String oldDn, String newDn)

oldDn: The old DN to be renamed, representing an entry in the LDAP directory.
newDn: New DN, indicating the new location of the entry in the LDAP directory.

This method will move the specified entry from the old DN to the new DN, achieving the effect of renaming.

The demo is as follows:

public void renameUser(String oldDn, String newDn) {<!-- -->
        ldapTemplate.rename(oldDn, newDn);
    }

Note: The new DN must contain the new parent DN to ensure that the entry is moved correctly to its new location. For example, if you want to move a user from "ou=people,dc=example,dc=com" to "ou=otherPeople,dc=example,dc=com", the new DN should be "uid=john,ou=otherPeople,dc=example,dc=com".

12.ldapTemplate.modifyAttributes (used to modify the attribute values of entries in the LDAP directory)

public void modifyAttributes(String dn, ModificationItem[] mods)

mods: Array of modifications to be applied, representing modifications to be made to the entry.

A modification item (ModificationItem) consists of two properties: the modification operation type (DirContext.ADD_ATTRIBUTE, DirContext.REMOVE_ATTRIBUTE or DirContext.REPLACE_ATTRIBUTE) and the desired Modified attributes (Attribute).

The demo is as follows:

public void updateUserPassword(String userDn, String newPassword) {<!-- -->
        ModificationItem[] mods = new ModificationItem[1];
        Attribute attribute = new BasicAttribute("userPassword", newPassword);
        mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, attribute);
        ldapTemplate.modifyAttributes(userDn, mods);
    }

In the above example, the updateUserPassword method uses ldapTemplate.modifyAttributes to modify the user password based on the user DN (userDn). First, create an array of ModificationItem containing the modifications you want to make. In this example, it only contains one item: replacing the (DirContext.REPLACE_ATTRIBUTE)userPassword attribute with the new password. Then, pass this modification array to the ldapTemplate.modifyAttributes method to make modifications

13.ldapTemplate.authenticate (used to authenticate LDAP)

public boolean authenticate(String base, String filter, String password)

password: user password, used for authentication operations.

This method is used to verify whether the given user password matches the user with the specified conditions in LDAP. Returns true if the match is successful, false otherwise.

The demo is as follows:

public boolean authenticateUser(String username, String password) {<!-- -->
        String baseDn = "ou=people,dc=example,dc=com";
        String filter = "(uid=" + username + ")";
        return ldapTemplate.authenticate(baseDn, filter, password);
    }

In the above example, the authenticateUser method uses ldapTemplate.authenticate to authenticate the user. The base DN, filter and user password are required. This method will verify whether the provided username and password match the user with the specified conditions in LDAP. If the match is successful, it will return true, indicating that the authentication is passed.