Securing a Simple Spring Boot REST API with LDAP Authentication
Written on
Chapter 1: Introduction to Spring Boot and LDAP
In this guide, we will explore how to set up a basic Spring Boot REST API, referred to as Simple API, and implement security using LDAP (OpenLDAP). The application will feature two endpoints: /public and /secured.
Essentially, LDAP (Lightweight Directory Access Protocol) acts as a centralized database for user information and serves as an authentication tool. Additionally, it can manage role assignments for application users. OpenLDAP is an open-source version of the LDAP protocol.
Let’s dive in!
Prerequisites
To follow along with this tutorial, ensure that Docker is installed on your machine.
Creating the Simple API Application
We will begin by creating a Spring Boot application using Spring Initializr. Name the application spring-boot-ldap-simple-api and include the dependencies for Spring Web and Spring Security. We will utilize Spring Boot version 3.1.3 and Java 17. Here’s the link to set up your environment.
Click the GENERATE button to download the zip file. After unzipping, open the spring-boot-ldap-simple-api project in your preferred IDE. Now, let's start developing the necessary classes.
Adding LDAP Dependencies
Next, we will modify the pom.xml file to include the following LDAP dependencies (code highlighted):
<dependency>
<groupId>org.springframework.ldap</groupId>
<artifactId>spring-ldap-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-ldap</artifactId>
</dependency>
Creating the SimpleApiController Class
We will now create the SimpleApiController class within the package com.example.springbootldapsimpleapi with the following code:
package com.example.springbootldapsimpleapi;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.security.Principal;
@RestController
public class SimpleApiController {
@GetMapping("/public")
public String getPublic() {
return "Hello World, this is a public endpoint";}
@GetMapping("/secured")
public String getSecured(Principal principal) {
return "Hello %s, this is a secured endpoint".formatted(principal.getName());}
}
The SimpleApiController class contains two methods for handling GET requests. The getPublic() method responds to requests at the /public URL, returning "Hello World, this is a public endpoint." This endpoint is accessible without authentication. On the other hand, the getSecured() method responds to requests at /secured, returning a personalized message for authenticated users.
Setting Up the WebSecurityConfig Class
Next, we will implement the WebSecurityConfig class in the com.example.springbootldapsimpleapi package with the following content:
package com.example.springbootldapsimpleapi;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http.authorizeHttpRequests(
authorizeHttpRequests -> authorizeHttpRequests
.requestMatchers(HttpMethod.GET, "/secured").authenticated()
.requestMatchers(HttpMethod.GET, "/public").permitAll()
.anyRequest().denyAll()
)
.httpBasic(Customizer.withDefaults())
.sessionManagement(sessionManagement -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.build();
}
}
The annotations @Configuration and @EnableWebSecurity indicate that this class is a configuration class for Spring Security. The securityFilterChain method sets up the security rules for HTTP requests, specifying that requests to /secured require authentication, while /public can be accessed freely.
Creating the LdapAuthenticationConfig Class
Next, we will define the LdapAuthenticationConfig class in the same package:
package com.example.springbootldapsimpleapi;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.ldap.core.support.BaseLdapPathContextSource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.ldap.LdapBindAuthenticationManagerFactory;
import org.springframework.security.ldap.userdetails.PersonContextMapper;
@Configuration
@EnableWebSecurity
public class LdapAuthenticationConfig {
@Bean
public AuthenticationManager ldapAuthenticationManager(BaseLdapPathContextSource contextSource) {
LdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource);
factory.setUserDnPatterns(USER_DN_PATTERN);
factory.setUserDetailsContextMapper(new PersonContextMapper());
return factory.createAuthenticationManager();
}
private static final String USER_DN_PATTERN = "uid={0}";
}
This class configures bind authentication, where the LDAP server verifies user credentials provided during login. This method ensures sensitive information remains secure.
Updating the Application Properties
Finally, we will modify the application.properties file with the following configurations:
spring.application.name=simple-api
spring.ldap.urls=ldap://localhost:389
spring.ldap.base=ou=users,dc=mycompany,dc=com
spring.ldap.username=cn=admin,dc=mycompany,dc=com
spring.ldap.password=admin
spring.ldap.anonymousReadOnly=true
logging.level.org.springframework.security=DEBUG
Overview of Properties:
- spring.application.name: Application name specification.
- spring.ldap.urls: LDAP server address.
- spring.ldap.base: Starting point in the LDAP directory for user information.
- spring.ldap.username: Username for LDAP access.
- spring.ldap.password: Password for LDAP access.
- spring.ldap.anonymousReadOnly: Allows public read access to LDAP.
- logging.level.org.springframework.security: Sets detailed logging for security events.
Implementing Tests
Starting OpenLDAP
To initiate the OpenLDAP Docker container, execute the following command in a terminal:
docker run --rm --name openldap
-p 389:389
-e LDAP_ORGANISATION="MyCompany Inc."
-e LDAP_DOMAIN=mycompany.com
osixia/openldap:1.5.0
Creating and Importing OpenLDAP Users
Create an LDIF file named ldap-mycompany-com.ldif in the root folder of the Simple API application containing user definitions.
To import this LDIF file into OpenLDAP, use the command:
ldapadd -x -D "cn=admin,dc=mycompany,dc=com" -w admin -H ldap://
-f ldap-mycompany-com.ldif
You can verify the imported user with:
ldapsearch -x -D "cn=admin,dc=mycompany,dc=com"
-w admin -H ldap://localhost:389
-b "ou=users,dc=mycompany,dc=com"
-s sub "(uid=*)"
Starting the Simple API
In a terminal, navigate to the root folder of the Simple API and run:
./mvnw clean spring-boot:run
Testing the Endpoints
Now that everything is set up, let’s test the endpoints. First, call the /public endpoint:
curl -i http://localhost:8080/public
Expected response:
HTTP/1.1 200
...
Hello World, this is a public endpoint
Next, attempt to access the /secured endpoint without credentials:
curl -i http://localhost:8080/secured
You should receive:
HTTP/1.1 401
...
Now, try accessing the /secured endpoint with valid credentials:
curl -i -u app-user:123 localhost:8080/secured
The expected response should be:
HTTP/1.1 200
...
Hello app-user, this is a secured endpoint
For invalid credentials, you can test with:
curl -i -u app-admin:123 localhost:8080/secured
curl -i -u app-user:124 localhost:8080/secured
In both cases, you should see:
HTTP/1.1 401
...
Shutting Down the Simple API and OpenLDAP
To stop the Simple API, press Ctrl+C in the terminal where it’s running. Similarly, do the same in the terminal running OpenLDAP.
Conclusion
In this guide, we have configured OpenLDAP as an identity provider for a Simple Spring Boot API, utilizing the Spring Security framework to secure endpoints. We also tested the application’s endpoints to confirm that the /secured endpoint is protected.
Support and Engagement
If you found this article helpful, please consider supporting it by:
- Engaging with comments, claps, or questions.
- Sharing on social media.
- Following me on Medium, LinkedIn, and Twitter.
- Subscribing to my newsletter for updates on new posts.
Chapter 2: Additional Resources
This video tutorial covers the basics of integrating Spring Boot, Spring Security, and LDAP from scratch.
This video goes in-depth on setting up Spring Boot LDAP Authentication with Spring Security and an LDAP server.