You are currently viewing Working with Security Filters in Spring Security: A Tutorial

Working with Security Filters in Spring Security: A Tutorial

Introduction

Security filters play a crucial role in Spring Security by intercepting and processing HTTP requests before they reach your application’s endpoints. They are used for various security-related tasks such as authentication, authorization, logging, and request modification. This tutorial will guide you through the process of working with security filters in Spring Security, including creating custom filters and configuring them in your application.

Prerequisites

Before starting, ensure you have the following installed:

  • JDK 8 or later
  • Maven or Gradle
  • An IDE like IntelliJ IDEA or Eclipse
  • Basic knowledge of Spring Boot and Spring Security

Step 1: Set Up Your Spring Boot Project

First, create a new Spring Boot project. You can use Spring Initializr (https://start.spring.io/) to generate the project. Select the following dependencies:

  • Spring Web
  • Spring Security

Download the project and import it into your IDE.

Step 2: Basic Spring Security Configuration

Start by creating a basic Spring Security configuration. Create a configuration class in src/main/java/com/example/demo/config/SecurityConfig.java:

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.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/public/**").permitAll()
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .loginPage("/login")
                .permitAll()
                .and()
            .logout()
                .permitAll();
    }

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

Step 3: Creating a Custom Security Filter

Next, create a custom security filter. This filter will log each incoming request. Create a class in src/main/java/com/example/demo/filter/LoggingFilter.java:

package com.example.demo.filter;

import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class LoggingFilter extends HttpFilter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // Filter initialization code if necessary
    }

    @Override
    protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        System.out.println("Request URL: " + request.getRequestURL());
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {
        // Filter cleanup code if necessary
    }
}

Step 4: Registering the Custom Filter

To register the custom filter, modify the security configuration class to include the filter. Update SecurityConfig as follows:

package com.example.demo.config;

import com.example.demo.filter.LoggingFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
@EnableWebSecurity
@Order(1)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .addFilterBefore(new LoggingFilter(), UsernamePasswordAuthenticationFilter.class)
            .authorizeRequests()
                .antMatchers("/public/**").permitAll()
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .loginPage("/login")
                .permitAll()
                .and()
            .logout()
                .permitAll();
    }

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

Here, the addFilterBefore method is used to add the LoggingFilter before the UsernamePasswordAuthenticationFilter.

Step 5: Testing the Custom Filter

Create simple controllers and views to test the custom filter. Create a controller in src/main/java/com/example/demo/controller/HomeController.java:

package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class HomeController {

    @GetMapping("/")
    public String home() {
        return "home";
    }

    @GetMapping("/public")
    public String publicPage() {
        return "public";
    }

    @GetMapping("/login")
    public String login() {
        return "login";
    }
}

Create simple Thymeleaf templates for the views in src/main/resources/templates/.

home.html:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Home</title>
</head>
<body>
    <h1>Home Page</h1>
    <a href="/public">Public Page</a><br>
    <a href="/logout">Logout</a>
</body>
</html>

public.html:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Public Page</title>
</head>
<body>
    <h1>Public Page</h1>
    <a href="/">Home</a>
</body>
</html>

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 for="username">Username:</label>
            <input type="text" id="username" name="username"/>
        </div>
        <div>
            <label for="password">Password:</label>
            <input type="password" id="password" name="password"/>
        </div>
        <div>
            <button type="submit">Login</button>
        </div>
    </form>
</body>
</html>

Run your Spring Boot application and navigate to http://localhost:8080. Check the console output to see the log messages from your custom filter.

Step 6: Advanced Filter Configuration

You can also create filters that handle authentication and authorization more specifically. For instance, you can create a filter that checks for a specific header or token.

Example: Custom Authentication Filter

Create a custom authentication filter in src/main/java/com/example/demo/filter/CustomAuthenticationFilter.java:

package com.example.demo.filter;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;

import javax.servlet.http.HttpServletRequest;

public class CustomAuthenticationFilter extends AbstractPreAuthenticatedProcessingFilter {

    @Override
    protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
        // Extract the principal (e.g., user) from the request, such as from a header
        return request.getHeader("X-Auth-User");
    }

    @Override
    protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
        // Return a dummy credential since this is pre-authenticated
        return "N/A";
    }
}

Registering the Custom Authentication Filter

Update the security configuration to register the custom authentication filter:

package com.example.demo.config;

import com.example.demo.filter.CustomAuthenticationFilter;
import com.example.demo.filter.LoggingFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider;

@Configuration
@EnableWebSecurity
@Order(1)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .addFilterBefore(new LoggingFilter(), UsernamePasswordAuthenticationFilter.class)
            .addFilterBefore(customAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
            .authorizeRequests()
                .antMatchers("/public/**").permitAll()
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .loginPage("/login")
                .permitAll()
                .and()
            .logout()
                .permitAll();
    }

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

    @Bean
    public CustomAuthenticationFilter customAuthenticationFilter() throws Exception {
        CustomAuthenticationFilter filter = new CustomAuthenticationFilter();
        filter.setAuthenticationManager(authenticationManager());
        return filter;
    }

    @Bean
    public PreAuthenticatedAuthenticationProvider preAuthenticatedAuthenticationProvider() {
        PreAuthenticatedAuthenticationProvider provider = new PreAuthenticatedAuthenticationProvider();
        provider.setPreAuthenticatedUserDetailsService(userDetailsServiceWrapper());
        return provider;
    }

    @

Bean
    public UserDetailsByNameServiceWrapper<PreAuthenticatedAuthenticationToken> userDetailsServiceWrapper() {
        return new UserDetailsByNameServiceWrapper<>(userDetailsService());
    }
}

Conclusion

In this tutorial, you have learned how to work with security filters in Spring Security. You created a custom logging filter, registered it in the security configuration, and explored the creation of a custom authentication filter. Filters are powerful tools in Spring Security that allow you to process requests in a flexible and efficient manner, enhancing the security and functionality of your application.

Leave a Reply