SecurityFilterChain in Spring Boot 3

In this blog post, we will explore the SecurityFilterChain in Spring Boot 3.3 using Spring Security 6.2. We will start with an introduction to SecurityFilterChain, followed by explanations and examples of form-based authentication, Basic Authentication, in-memory authentication, role-based authorization, and database authentication. Finally, we will delve into how it works to secure your Spring Boot applications.

Introduction to SecurityFilterChain

Spring Security is a powerful framework that provides authentication, authorization, and protection against common security attacks. One of the core components of Spring Security is the SecurityFilterChain, which is responsible for applying security filters to HTTP requests.

A SecurityFilterChain is essentially a series of filters that process security-related aspects of HTTP requests and responses. These filters can handle tasks such as authentication, authorization, and session management. By configuring these filters, you can define how your application should respond to different security scenarios.

Configuring SecurityFilterChain in Spring Boot 3.3

Form-Based Authentication

Form-based authentication is commonly used in web applications where users log in through a form.

Configuration Example

package com.example.security;

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.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

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

        return http.build();
    }

    @Bean
    public UserDetailsService userDetailsService() {
        UserDetails user =
             User.withDefaultPasswordEncoder()
                .username("user")
                .password("password")
                .roles("USER")
                .build();

        return new InMemoryUserDetailsManager(user);
    }
}

Explanation

  1. authorizeHttpRequests: Configures URL authorization rules.

    • requestMatchers("/", "/home").permitAll(): Allows all requests to the root and home page without authentication.
    • anyRequest().authenticated(): Requires authentication for all other requests.
  2. formLogin: Configures form-based login.

    • loginPage("/login"): Specifies the custom login page URL.
    • permitAll(): Allows access to the login page for all users.
  3. logout: Configures logout functionality.

    • permitAll(): Allows access to the logout URL for all users.
  4. UserDetailsService: Configures an in-memory user store.

    • User.withDefaultPasswordEncoder().username("user").password("password").roles("USER").build(): Creates an in-memory user with the username user, password password, and role USER.

Basic Authentication

Basic Authentication is suitable for securing REST APIs where credentials are passed with each request.

Configuration Example

package com.example.security;

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.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .csrf(csrf -> csrf.disable())
            .cors(cors -> cors.disable()) // Disable CORS
            .authorizeHttpRequests((requests) -> requests
                .requestMatchers("/api/public/**").permitAll()
                .anyRequest().authenticated()
            )
            .httpBasic();

        return http.build();
    }

    @Bean
    public UserDetailsService userDetailsService() {
        UserDetails user =
             User.withDefaultPasswordEncoder()
                .username("user")
                .password("password")
                .roles("USER")
                .build();

        return new InMemoryUserDetailsManager(user);
    }
}

Explanation

  1. csrf(csrf -> csrf.disable()): Disables CSRF protection, which is typically not needed for REST APIs.

  2. cors(cors -> cors.disable()): Disables CORS.

  3. authorizeHttpRequests: Configures URL authorization rules.

    • requestMatchers("/api/public/**").permitAll(): Allows all requests to URLs under /api/public/ to be publicly accessible.
    • anyRequest().authenticated(): Requires authentication for all other requests.
  4. httpBasic(): Configures HTTP Basic Authentication for securing REST endpoints.

  5. UserDetailsService: Configures an in-memory user store.

    • User.withDefaultPasswordEncoder().username("user").password("password").roles("USER").build(): Creates an in-memory user with the username user, password password, and role USER.

In-Memory Authentication

In-memory authentication is useful for simple applications or for testing purposes.

Configuration Example

package com.example.security;

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.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

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

        return http.build();
    }

    @Bean
    public UserDetailsService userDetailsService() {
        UserDetails user =
             User.withDefaultPasswordEncoder()
                .username("user")
                .password("password")
                .roles("USER")
                .build();

        return new InMemoryUserDetailsManager(user);
    }
}

Explanation

  1. authorizeHttpRequests: Configures URL authorization rules.

    • requestMatchers("/", "/home").permitAll(): Allows all requests to the root and home page without authentication.
    • anyRequest().authenticated(): Requires authentication for all other requests.
  2. formLogin: Configures form-based login.

    • loginPage("/login"): Specifies the custom login page URL.
    • permitAll(): Allows access to the login page for all users.
  3. logout: Configures logout functionality.

    • permitAll(): Allows access to the logout URL for all users.
  4. UserDetailsService: Configures an in-memory user store.

    • User.withDefaultPasswordEncoder().username("user").password("password").roles("USER").build(): Creates an in-memory user with the username user, password password, and role USER.

Role-Based Authorization

Role-based authorization restricts access to resources based on user roles.

Configuration Example

package com.example.security;

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.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests((requests) -> requests
                .requestMatchers("/", "/home").permitAll()
                .requestMatchers("/admin/**").hasRole("ADMIN")
                .anyRequest().authenticated()
            )
            .formLogin((form) -> form
                .loginPage("/login")
                .permitAll()
            )
            .logout((logout) -> logout
                .permitAll()
            );

        return http.build();
    }

    @Bean
    public UserDetailsService userDetailsService() {
        UserDetails user =
             User.withDefaultPasswordEncoder()
                .username("user")
                .password("password")
                .roles("USER")
                .build();

        UserDetails admin =
             User.withDefaultPasswordEncoder()
                .username("admin")
                .password("admin")
                .roles("ADMIN")
                .build();

        return new InMemoryUserDetailsManager(user, admin);
    }
}

Explanation

  1. authorizeHttpRequests: Configures URL authorization rules.
    • requestMatchers("/", "/home").permitAll(): Allows all requests to the root and home page without authentication.
    • `requestMatchers("/admin/**").hasRole

("ADMIN"): Restricts access to URLs under/admin/to users with theADMIN` role.

  • anyRequest().authenticated(): Requires authentication for all other requests.
  1. formLogin: Configures form-based login.

    • loginPage("/login"): Specifies the custom login page URL.
    • permitAll(): Allows access to the login page for all users.
  2. logout: Configures logout functionality.

    • permitAll(): Allows access to the logout URL for all users.
  3. UserDetailsService: Configures an in-memory user store.

    • User.withDefaultPasswordEncoder().username("user").password("password").roles("USER").build(): Creates an in-memory user with the username user, password password, and role USER.
    • User.withDefaultPasswordEncoder().username("admin").password("admin").roles("ADMIN").build(): Creates an in-memory user with the username admin, password admin, and role ADMIN.

Database Authentication

Database authentication is used for real-world applications where user details are stored in a database.

Configuration Example

package com.example.security;

import jakarta.sql.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.JdbcUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .csrf(csrf -> csrf.disable())
            .cors(cors -> cors.disable()) // Disable CORS
            .authorizeHttpRequests((authorize) -> {
                authorize.requestMatchers("/api/auth/**").permitAll();
                authorize.anyRequest().authenticated();
            })
            .httpBasic(Customizer.withDefaults());

        return http.build();
    }

    @Bean
    public UserDetailsService userDetailsService(DataSource dataSource) {
        return new JdbcUserDetailsManager(dataSource);
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
        return configuration.getAuthenticationManager();
    }

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

Explanation

  1. csrf(csrf -> csrf.disable()): Disables CSRF protection.

  2. cors(cors -> cors.disable()): Disables CORS.

  3. authorizeHttpRequests: Configures URL authorization rules.

    • authorize.requestMatchers("/api/auth/**").permitAll(): Allows all requests to URLs under /api/auth/ to be publicly accessible.
    • authorize.anyRequest().authenticated(): Requires authentication for all other requests.
  4. httpBasic(Customizer.withDefaults()): Configures HTTP Basic Authentication for securing REST endpoints.

  5. UserDetailsService: Configures a database user store.

    • new JdbcUserDetailsManager(dataSource): Creates a JdbcUserDetailsManager with the provided DataSource to fetch user details from the database.
  6. AuthenticationManager: Configures the AuthenticationManager to use the UserDetailsService.

    • configuration.getAuthenticationManager(): Retrieves the AuthenticationManager from the AuthenticationConfiguration.
  7. PasswordEncoder: Configures the password encoder.

    • new BCryptPasswordEncoder(): Creates a BCryptPasswordEncoder for encoding passwords.

How SecurityFilterChain Works

The SecurityFilterChain works by intercepting incoming HTTP requests and applying a series of filters to them. Here’s a step-by-step overview of how it operates:

  1. Request Interception: When an HTTP request is made to the application, the SecurityFilterChain intercepts it.
  2. Filter Processing: The intercepted request is passed through a chain of filters. Each filter performs a specific security-related function, such as authentication or authorization.
  3. Authentication: If the request requires authentication, the filters will check the request for valid credentials. If the credentials are valid, the request proceeds. If not, the user is redirected to the login page (for form login) or receives a 401 Unauthorized response (for REST APIs).
  4. Authorization: The filters also check if the authenticated user has the necessary permissions to access the requested resource. If the user is authorized, the request proceeds. If not, the user is denied access.
  5. Response Handling: After all filters have processed the request, the response is generated and sent back to the client.

Conclusion

The SecurityFilterChain is a crucial component of Spring Security that ensures your Spring Boot applications are secure by handling authentication, authorization, and other security-related tasks. By configuring a custom SecurityFilterChain, you can define how your application should handle different security scenarios, providing a robust security framework for your web applications and REST APIs.

In this blog post, we covered the basics of SecurityFilterChain, provided example configurations for form-based authentication, Basic Authentication, in-memory authentication, role-based authorization, and database authentication, and explained how it works. With this knowledge, you can start implementing and customizing security for your Spring Boot applications using Spring Security 6.2.

Comments