Spring Boot Security Auto-Configuration

Spring Boot provides a set of auto-configuration classes to simplify security configuration. Starting with Spring Boot 3.2, some methods and classes have been deprecated or replaced, and the security configuration has been streamlined. In this tutorial, we'll walk through the process of setting up security in a Spring Boot 3.2 application using the new recommended approach.

Prerequisites

  • JDK 17 or later
  • Maven or Gradle
  • IDE (IntelliJ IDEA, Eclipse, etc.)

Step 1: Set Up a Spring Boot Project

1.1 Create a New Spring Boot Project

Use Spring Initializr to create a new project with the following dependencies:

  • Spring Web
  • Spring Security

Download and unzip the project, then open it in your IDE.

1.2 Configure application.properties

Set up the application properties for your project. This file is located in the src/main/resources directory.

# src/main/resources/application.properties

server.port=8080

Step 2: Implement Security Configuration

2.1 Create a Security Configuration Class

In Spring Boot 3.2, the way to configure security has changed. The authorizeRequests() method has been replaced with authorizeHttpRequests(), and antMatchers() has been replaced with requestMatchers(). We will use these new methods to set up our security configuration.

package com.example.demo.config;

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.web.SecurityFilterChain;

@Configuration
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests((requests) -> requests
                .requestMatchers("/public/**").permitAll()
                .anyRequest().authenticated()
            )
            .formLogin((form) -> form
                .loginPage("/login")
                .permitAll()
            )
            .logout((logout) -> logout
                .permitAll()
            );
        return http.build();
    }
}

Explanation:

  • @Configuration: Marks this class as a source of bean definitions.
  • SecurityFilterChain: Configures the security filter chain.
  • authorizeHttpRequests(): Replaces the deprecated authorizeRequests().
  • requestMatchers(): Replaces the deprecated antMatchers() for URL matching.
  • formLogin(): Configures form-based authentication.
  • logout(): Configures the logout functionality.

2.2 Create a Custom Login Page

Create a custom login page in the src/main/resources/templates directory. If you're not using Thymeleaf, adjust the path and file type as necessary.

<!-- src/main/resources/templates/login.html -->

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Login</title>
</head>
<body>
    <h1>Login</h1>
    <form th:action="@{/login}" method="post">
        <div>
            <label>Username:</label>
            <input type="text" name="username"/>
        </div>
        <div>
            <label>Password:</label>
            <input type="password" name="password"/>
        </div>
        <div>
            <button type="submit">Login</button>
        </div>
    </form>
</body>
</html>

2.3 Create a Controller for Public Access

Create a controller to handle public and secure endpoints.

package com.example.demo.controller;

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

@RestController
public class DemoController {

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

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

Explanation:

  • @RestController: Marks the class as a REST controller.
  • @GetMapping("/public"): Maps GET requests to the publicEndpoint method.
  • @GetMapping("/secure"): Maps GET requests to the secureEndpoint method.

Step 3: Running and Testing the Application

3.1 Run the Application

Run the Spring Boot application using your IDE or the command line:

./mvnw spring-boot:run

3.2 Test the Security Configuration

  1. Open your browser and navigate to http://localhost:8080/public. You should see the message "This is a public endpoint."

  2. Navigate to http://localhost:8080/secure. You should be redirected to the login page.

  3. Log in using the default credentials provided by Spring Security (user and a generated password printed in the console).

  4. After logging in, you should see the message "This is a secure endpoint."

Step 4: Customizing User Details

4.1 Create a Custom UserDetailsService

To provide custom user details, you can implement the UserDetailsService interface.

package com.example.demo.service;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

@Configuration
public class UserDetailsServiceConfig {

    @Bean
    public UserDetailsService userDetailsService() {
        var userDetailsService = new InMemoryUserDetailsManager();

        var user = User.withUsername("user")
            .password("{noop}password") // {noop} indicates no encoding
            .roles("USER")
            .build();

        userDetailsService.createUser(user);

        return userDetailsService;
    }
}

Explanation:

  • InMemoryUserDetailsManager: An implementation of UserDetailsService that stores user details in memory.
  • User.withUsername("user"): Creates a user with the username "user" and password "password" (no encoding).

4.2 Update Security Configuration

Update the SecurityConfig class to use the custom UserDetailsService.

package com.example.demo.config;

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.core.userdetails.UserDetailsService;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
public class SecurityConfig {

    private final UserDetailsService userDetailsService;

    public SecurityConfig(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests((requests) -> requests
                .requestMatchers("/public/**").permitAll()
                .anyRequest().authenticated()
            )
            .formLogin((form) -> form
                .loginPage("/login")
                .permitAll()
            )
            .logout((logout) -> logout
                .permitAll()
            )
            .userDetailsService(userDetailsService);
        return http.build();
    }
}

Explanation:

  • The SecurityConfig constructor now takes a UserDetailsService parameter.
  • The userDetailsService method is added to the security filter chain configuration.

Conclusion

In this tutorial, you have learned how to set up and configure security in a Spring Boot 3.2 application using the new recommended approach. We covered:

  • Creating a custom security configuration class.
  • Setting up a custom login page.
  • Creating a controller for public and secure endpoints.
  • Running and testing the security configuration.
  • Customizing user details with a custom UserDetailsService.

By following these steps, you can secure your Spring Boot applications and control access to different parts of your application.


Comments