Spring Boot 3.4 provides a powerful and flexible way to intercept and secure HTTP requests before they reach your application logic. In modern web applications, having the ability to inspect, validate, and authenticate incoming requests is essential — whether for enforcing authentication, logging, or monitoring. In this post, we’ll create a complete, runnable Spring Boot 3.4 project that shows how to setup a WebRequest Filter in Spring Boot 3.4 to intercept every incoming request and validate security credentials before allowing access.


What We’ll Build

We’ll create a small but complete Spring Boot application that:

  • Exposes a public endpoint (/public) accessible by anyone
  • Exposes a protected endpoint (/secure) accessible only with a valid API key
  • Uses a custom WebRequest filter to intercept and validate every request
  • Responds with 401 Unauthorized when credentials are missing or invalid

By the end, you’ll have a runnable demo that illustrates how Spring Boot filters work — and how to use them to implement lightweight authentication mechanisms.


Background: Understanding Spring Security and Filters

Spring Security is a powerful and highly customizable framework within the Spring ecosystem designed to handle authentication, authorization, and security enforcement for Java applications. It provides the foundation for building secure APIs and web applications by managing how users and systems access resources.

At its core, Spring Security uses a chain of filters that intercept each web request before it reaches your application’s controllers. Each filter has a specific role — such as validating a JWT token, checking user sessions, enforcing CSRF protection, or handling login forms. This filter-based architecture allows developers to insert custom filters anywhere in the chain to implement custom logic like logging, auditing, or header validation.

Key components of Spring Security include:

  • AuthenticationManager – Handles authentication logic and user validation.
  • SecurityFilterChain – Defines how filters are applied to incoming requests.
  • UserDetailsService – Loads user-specific data from a database or service.
  • PasswordEncoder – Hashes and validates passwords (e.g., BCrypt).
  • GrantedAuthority / Roles – Defines what each authenticated user is allowed to do.

In this example, we’ll focus on the filter mechanism, showing how to create a custom filter using OncePerRequestFilter — one of the core components used internally by Spring Security itself.


WebRequest Filter in Spring Boot 3.4 – Project Setup

Dependencies:

  • Spring Boot 3.4.x
  • Spring Web
  • (Optional) Spring Boot Starter Test

Create a new Spring Boot Maven project using Spring Initializr or manually with the following dependencies:

Project: Maven  
Language: Java  
Spring Boot: 3.4.0  
Dependencies: Spring Web

Directory Structure

webrequest-filter-demo/
 ├── src/
 │   ├── main/
 │   │   ├── java/
 │   │   │   └── com/example/filterdemo/
 │   │   │       ├── FilterDemoApplication.java
 │   │   │       ├── filters/
 │   │   │       │   └── ApiKeyAuthFilter.java
 │   │   │       └── controllers/
 │   │   │           └── DemoController.java
 │   │   └── resources/
 │   │       └── application.yml
 └── pom.xml

Step 1: pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>filterdemo</artifactId>
    <version>1.0.0</version>
    <name>WebRequest Filter Demo</name>

    <properties>
        <java.version>17</java.version>
        <spring.boot.version>3.4.0</spring.boot.version>
    </properties>

    <dependencies>
        <!-- Spring Boot Web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- For testing -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!-- Spring Boot Maven plugin -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>
XML

Step 2: Application Entry Point

package com.example.filterdemo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class FilterDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(FilterDemoApplication.class, args);
    }
}
Java

Step 3: Implement a Custom Filter

We’ll use OncePerRequestFilter from org.springframework.web.filter — this ensures that your filter executes once per request, even if the request passes through multiple dispatch phases.

package com.example.filterdemo.filters;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

@Component
public class ApiKeyAuthFilter extends OncePerRequestFilter {

    private static final String API_KEY_HEADER = "X-API-KEY";
    private static final String EXPECTED_API_KEY = "my-secret-key";

    @Override
    protected void doFilterInternal(
            HttpServletRequest request,
            HttpServletResponse response,
            FilterChain filterChain)
            throws ServletException, IOException {

        String apiKey = request.getHeader(API_KEY_HEADER);

        // Allow public endpoints to bypass authentication
        String path = request.getRequestURI();
        if (path.startsWith("/public")) {
            filterChain.doFilter(request, response);
            return;
        }

        // Check API key for secure endpoints
        if (apiKey == null || !EXPECTED_API_KEY.equals(apiKey)) {
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
            response.getWriter().write("Unauthorized: Invalid or missing API key");
            return;
        }

        // Continue with the next filter in the chain
        filterChain.doFilter(request, response);
    }
}
Java

Explanation:

  • The filter runs for every HTTP request.
  • Requests to /public skip authentication.
  • Requests to /secure must include the correct X-API-KEY header.
  • Unauthorized requests are immediately rejected.

Step 4: Controller with Public and Secure Endpoints

package com.example.filterdemo.controllers;

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. No API key required.";
    }

    @GetMapping("/secure")
    public String secureEndpoint() {
        return "🔒 This is a secure endpoint. You provided a valid API key!";
    }
}
Java

Step 5: Optional application.yml

server:
  port: 8080
spring:
  application:
    name: webrequest-filter-demo
YAML

Step 6: Run and Test

Run:

mvn spring-boot:run

Test your endpoints:

Public Endpoint

curl http://localhost:8080/public

Response:

This is a public endpoint. No API key required.

Secure Endpoint (No Key)

curl http://localhost:8080/secure

Response:

Unauthorized: Invalid or missing API key

Secure Endpoint (With Key)

curl -H "X-API-KEY: my-secret-key" http://localhost:8080/secure

Response:

This is a secure endpoint. You provided a valid API key!

How It Works

Spring Boot automatically registers every Filter bean in the application context.
The OncePerRequestFilter ensures your logic executes exactly once per HTTP request.

When a request comes in:

  1. The ApiKeyAuthFilter runs first.
  2. It validates the X-API-KEY header.
  3. If the key is missing or invalid, the filter stops the chain and returns a 401.
  4. Otherwise, the request continues to the controller.

Next Steps

You can easily extend this filter to:

  • Validate JWT tokens instead of static keys
  • Integrate with Spring Security’s SecurityFilterChain
  • Load keys or tokens from a database
  • Apply different filters for different URL patterns

For configurable keys:

app:
  api-key: my-secret-key

and inject it:

@Value("${app.api-key}")
private String expectedKey;

Conclusion

With Spring Boot 3.4, building custom request filters has never been simpler. Using OncePerRequestFilter, you can intercept and secure all incoming requests in a clean, reusable, and testable way.

Combined with Spring Security’s powerful filter chain, this approach forms the foundation for building robust, secure, and scalable Java APIs.

You now have a complete working example demonstrating how to:

  • Intercept every HTTP request
  • Validate API credentials
  • Control access to public and private endpoints

While you are here, maybe try one of my apps for the iPhone.

Snap! I was there on the App Store

Listed below are the links to the first two parts of this series

How to build a blog engine with React & Spring Boot – Part 1 – My Day To-Do (mydaytodo.com)

How to build a blog engine with React & Spring Boot – Part 2 – My Day To-Do (mydaytodo.com)

Here are some of my other bloposts,

How to unit test react-redux app – My Day To-Do (mydaytodo.com)


0 Comments

Leave a Reply

Verified by MonsterInsights