Implementing Spring Security in a Spring Reactive

Spring Security is a comprehensive framework that secures Java-based applications, providing authentication, authorization, and protection against common security vulnerabilities. As reactive programming becomes increasingly popular, Spring Security has adapted to support reactive applications developed using Spring WebFlux. This blog will guide you through the step-by-step implementation of Spring Security in a Spring Reactive Maven project.

Prerequisites

Before we start, ensure you have the following:

1. Java 17 or higher: Necessary to work with the latest Spring Boot versions.

2. Maven: The build tool used for dependency management and project lifecycle.

3. Basic Knowledge: Familiarity with Spring WebFlux, reactive programming concepts, and security principles.

Additionally, initialize a Maven project with Spring Boot and WebFlux dependencies, which we will build upon in this tutorial.

1️⃣ Adding Dependencies

The first step is to include the necessary dependencies in your pom.xml file. These dependencies enable WebFlux, Spring Security, and testing for reactive programming.

<dependencies>
<!-- Spring Boot Starter WebFlux -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

<!-- Spring Boot Starter Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

<!-- Spring Boot Starter Test for reactive testing -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>

<!-- Reactor Test for verifying reactive streams -->
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

These dependencies enable us to:

1. Build a reactive web application with spring-boot-starter-webflux.

2. Add authentication and authorization using spring-boot-starter-security.

3. Test reactive components effectively with spring-boot-starter-test and reactor-test.

Related read: Reactive Programming with Spring WebFlux Guide

2️⃣ Configuring Security

To define how your application handles security, create a custom configuration class.

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.server.SecurityWebFilterChain;

@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {

@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
return http
.csrf().disable()
.authorizeExchange()
.pathMatchers("/public/**").permitAll()
.anyExchange().authenticated()
.and()
.httpBasic()
.and()
.build();
}

@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}

➡️ Key Points

1. Disabling CSRF: Cross-Site Request Forgery protection is disabled to simplify the tutorial, but it should be enabled in production.

2. Authorization Rules:

  • Paths starting with /public/** are open to everyone.
  • All other paths require authenticated access.

3. Authentication Mechanisms: Supports HTTP Basic authentication.

4. Password Encoder: BCrypt is used to encode passwords securely, ensuring they’re not stored in plain text.

Transform Your App's Scalability with Reactive Spring - Hire Our Developers Now!

3️⃣ Creating a Reactive User Details Service

Spring Security requires a mechanism to fetch user details during authentication. In a reactive application, we use a ReactiveUserDetailsService.

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import reactor.core.publisher.Mono;

@Configuration
public class UserDetailsServiceConfig {

@Bean
public ReactiveUserDetailsService userDetailsService() {
return username -> {
// Simulate fetching user details from a database.
if ("user".equals(username)) {
UserDetails user = User.builder()
.username("user")
.password(new BCryptPasswordEncoder().encode("password"))
.roles("USER")
.build();
return Mono.just(user);
}
return Mono.empty(); // No user found.
};
}
}

➡️ Explanation

1. The ReactiveUserDetailsService implementation fetches user information reactively.

2. In this example, a user with the username user and password password is defined.

3. Passwords are encoded with BCrypt for security.

4. In a real-world application, user details would be retrieved from a database.

4️⃣ Defining REST Endpoints

To verify the security configuration, define some basic endpoints.

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;

@RestController
@RequestMapping("/api")
public class TestController {

@GetMapping("/public")
public Mono<String> publicEndpoint() {
return Mono.just("This is a public endpoint.");
}

@GetMapping("/secure")
public Mono<String> secureEndpoint() {
return Mono.just("This is a secure endpoint.");
}
}

➡️ Details

1. /api/public: Open to everyone without authentication.

2. /api/secure: Accessible only to authenticated users.

5️⃣ Testing the Application

Running the Application

Start the Spring Boot application and test the endpoints using tools like Postman or curl:

Access Public Endpoint

🔹 curl http://localhost:8080/api/public

1. Output: This is a public endpoint.

Access Secure Endpoint without Authentication

🔹 curl http://localhost:8080/api/secure

2. Output: HTTP 401 Unauthorized.

Access Secure Endpoint with Authentication

🔹 curl -u user:password http://localhost:8080/api/secure

3. Output: This is a secure endpoint.

coma

Conclusion

Spring Security is essential for building secure, scalable applications by providing robust authentication and authorization. In reactive programming, it integrates seamlessly with Spring WebFlux to enhance performance and scalability.

Its applications include securing APIs, microservices, and real-time systems. Looking ahead, advancements like zero-trust security and improved OAuth2 support will further expand its capabilities. By integrating Spring Security into your reactive projects, you can ensure robust protection for your applications.

You can find the complete source code for this example in my GitHub repository.

Keep Reading

Keep Reading

  • Service
  • Career
  • Let's create something together!

  • We’re looking for the best. Are you in?