23 - Spring Filters
What are Filters?
A Filter is a component that intercepts HTTP requests and responses before they reach the controller or after the controller finishes processing.
Filters are part of the Servlet specification, so they operate at a lower level than Spring MVC controllers.
Filters are commonly used for:
- Request logging
- Authentication
- Authorization
- CORS
- Adding or modifying HTTP headers
- Request validation
- Measuring request execution time
- Modifying HTTP responses
- Blocking requests
- Security-related processing
Request Flow
A simplified Spring Boot request flow looks like this:
Client
|
v
HTTP Request
|
v
Servlet Container (Tomcat)
|
v
Filter Chain
|
+--> Filter 1
|
+--> Filter 2
|
+--> Filter 3
|
v
DispatcherServlet
|
v
Controller
|
v
Service
|
v
Repository
|
v
HTTP Response
A filter can execute logic before and after the next component in the chain.
For example:
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain) {
// Before the request continues
System.out.println("Request received");
chain.doFilter(request, response);
// After the request has been processed
System.out.println("Response generated");
}
The most important line is:
chain.doFilter(request, response);
This tells the application to continue processing the request and execute the next element in the chain.
If the filter does not call chain.doFilter(), the request can be stopped at that point.
Understanding the Filter Chain
A Filter Chain is an ordered sequence of filters through which an HTTP request passes.
For example:
Request
|
v
+----------------+
| Logging Filter |
+----------------+
|
v
+----------------+
| Auth Filter |
+----------------+
|
v
+----------------+
| CORS Filter |
+----------------+
|
v
+----------------+
| Dispatcher |
| Servlet |
+----------------+
|
v
Controller
Each filter receives the request and can:
- Process the request
- Modify the request
- Stop the request
- Pass the request to the next filter
- Process the response after the next component finishes
The FilterChain Interface
The Servlet API provides the FilterChain interface.
The most important method is:
chain.doFilter(request, response);
It passes the request to the next element in the chain.
A filter usually follows this pattern:
public void doFilter(...) {
// Logic before the next filter
chain.doFilter(request, response);
// Logic after the next filter
}
Before and After Execution
Imagine three filters:
Filter A
|
v
Filter B
|
v
Filter C
|
v
Controller
The execution order is similar to nested method calls:
Filter A - BEFORE
|
Filter B - BEFORE
|
Filter C - BEFORE
|
Controller
|
Filter C - AFTER
|
Filter B - AFTER
|
Filter A - AFTER
This happens because each filter calls chain.doFilter() and waits for the next component to finish.
Why Filter Order Matters
The order of filters can be very important.
For example:
Authentication Filter
|
v
Authorization Filter
|
v
Controller
Authentication normally needs to happen before authorization.
Why?
Because authorization needs to know:
- Who is the user?
- Is the user authenticated?
- What roles does the user have?
- What authorities does the user have?
Only after authentication can the application properly determine whether the user is authorized to perform an operation.
Implementing a Custom Filter
There are several ways to create a custom filter in Spring Boot.
A common approach is to extend:
OncePerRequestFilter
OncePerRequestFilter is provided by Spring and is commonly used when implementing custom HTTP filters.
It guarantees that the filter is invoked once per request dispatch.
Example: Request Logging Filter
Suppose we want to create a filter that logs every HTTP request.
Step 1 — Create the Filter
@Component
public class RequestLoggingFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(
HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
System.out.println(
"Request: " +
request.getMethod() +
" " +
request.getRequestURI()
);
filterChain.doFilter(request, response);
}
}
Because the class is annotated with:
@Component
Spring automatically detects the class as a Spring bean and registers the filter.
Step 2 — Continue the Filter Chain
The most important line is:
filterChain.doFilter(request, response);
Without this call, the request will not continue to the next filter or controller.
For example:
@Override
protected void doFilterInternal(
HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
System.out.println("Before controller");
filterChain.doFilter(request, response);
System.out.println("After controller");
}
The execution can be visualized as:
Request
|
v
Filter
|
| "Before controller"
|
v
Controller
|
v
Filter
|
| "After controller"
|
v
Response
Blocking a Request
A filter can also stop a request.
For example, suppose an application expects an API key.
@Component
public class ApiKeyFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(
HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
String apiKey = request.getHeader("X-API-KEY");
if (!"my-secret-key".equals(apiKey)) {
response.setStatus(
HttpServletResponse.SC_UNAUTHORIZED
);
return;
}
filterChain.doFilter(request, response);
}
}
If the API key is invalid:
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
return;
The filter does not call:
filterChain.doFilter(request, response);
Therefore, the request stops at the filter.
The flow becomes:
Request
|
v
API Key Filter
|
+---- Invalid ----> 401 Unauthorized
|
+---- Valid ------> Controller
This is one of the important characteristics of filters:
A filter can decide whether a request should continue.
Registering and Ordering Filters
If you need more control over how a filter is registered, you can use FilterRegistrationBean.
For example:
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<RequestLoggingFilter>
requestLoggingFilter(RequestLoggingFilter filter) {
FilterRegistrationBean<RequestLoggingFilter> registration =
new FilterRegistrationBean<>();
registration.setFilter(filter);
registration.addUrlPatterns("/api/*");
registration.setOrder(1);
return registration;
}
}
The following configuration:
registration.addUrlPatterns("/api/*");
means that the filter applies to URLs such as:
/api/users
/api/products
/api/orders
but not necessarily to endpoints outside /api.
The following configuration:
registration.setOrder(1);
controls the filter’s position relative to other servlet filters.
Filters vs Interceptors
Filters and Spring MVC Interceptors are similar, but they operate at different levels.
The general request flow is:
HTTP Request
|
v
Filter
|
v
DispatcherServlet
|
v
Interceptor
|
v
Controller
Filter
A Filter belongs to the Servlet layer.
It can execute before Spring MVC processes the request.
HTTP Request
|
v
Filter
|
v
Spring MVC
Typical use cases:
- Request logging
- Authentication
- Security
- CORS
- Request/response manipulation
- HTTP-level processing
Interceptor
An Interceptor belongs to the Spring MVC layer.
It operates around controller execution.
HTTP Request
|
v
Spring MVC
|
v
Interceptor
|
v
Controller
Typical use cases:
- Controller-specific validation
- Auditing controller execution
- Measuring controller execution time
- Adding model attributes
- Executing logic before or after controllers
General Rule
Use a Filter when you need to work at the HTTP/Servlet level.
Use an Interceptor when your logic is closely related to Spring MVC controllers.
A Glimpse at Spring Security Filters
Spring Security heavily relies on filters.
When Spring Security is enabled, HTTP requests pass through a Security Filter Chain before reaching the controller.
Conceptually:
Client
|
v
HTTP Request
|
v
Spring Security Filter Chain
|
+--> Security Filter
|
+--> Authentication Filter
|
+--> Authorization Filter
|
v
Controller
The exact filters and their order depend on the Spring Security configuration and the authentication mechanisms being used.
What is the Security Filter Chain?
The Security Filter Chain is a sequence of Spring Security filters responsible for processing security-related aspects of an HTTP request.
It can handle:
- Authentication
- Authorization
- Security context management
- CSRF protection
- Session management
- Bearer tokens
- HTTP Basic authentication
- Exception handling
- Security headers
A simplified flow is:
Request
|
v
Security Filter Chain
|
+--> Is the request authenticated?
|
+--> Who is the authenticated user?
|
+--> What authorities does the user have?
|
+--> Is the user authorized?
|
v
Controller
Authentication vs Authorization
Understanding the difference between authentication and authorization is fundamental when studying Spring Security filters.
Authentication
Authentication answers:
Who are you?
For example, a client might send:
Authorization: Bearer <JWT>
A Spring Security authentication filter can:
- Extract the token
- Validate the token
- Identify the user
- Create an
Authenticationobject - Store the authentication information in the
SecurityContext
Conceptually:
Request
|
v
Authentication Filter
|
+--> Extract credentials
|
+--> Validate credentials
|
+--> Identify user
|
v
SecurityContext
Authorization
Authorization answers:
Are you allowed to perform this operation?
For example:
@PreAuthorize("hasRole('ADMIN')")
A user can be authenticated but still not have permission to access a particular resource.
The relationship is:
Authentication
|
v
Who are you?
|
v
Authorization
|
v
What are you allowed to do?
Custom Filters with Spring Security
A common use case is creating a custom authentication filter.
For example, suppose an application receives a JWT:
Authorization: Bearer abc123
A custom filter could inspect the token.
@Component
public class CustomAuthenticationFilter
extends OncePerRequestFilter {
@Override
protected void doFilterInternal(
HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
String authorization =
request.getHeader("Authorization");
if (authorization != null &&
authorization.startsWith("Bearer ")) {
String token =
authorization.substring(7);
// Validate token
// Load user
// Create Authentication
// Set SecurityContext
}
filterChain.doFilter(request, response);
}
}
The filter can then be added to the Spring Security filter chain.
For example:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
SecurityFilterChain securityFilterChain(
HttpSecurity http,
CustomAuthenticationFilter customFilter)
throws Exception {
return http
.addFilterBefore(
customFilter,
UsernamePasswordAuthenticationFilter.class
)
.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/**")
.permitAll()
.anyRequest()
.authenticated()
)
.build();
}
}
The important method here is:
.addFilterBefore(...)
Spring Security also provides methods such as:
.addFilterAfter(...)
and:
.addFilterAt(...)
These methods allow you to control where a custom filter is positioned in the Security Filter Chain.
JWT Authentication Flow
A typical Spring Security application using JWT might follow this flow:
Client
|
| Authorization: Bearer <JWT>
v
Spring Security Filter Chain
|
v
JWT Authentication Filter
|
+--> Extract JWT
|
+--> Validate JWT
|
+--> Extract user information
|
+--> Create Authentication
|
+--> Store Authentication
| in SecurityContext
|
v
Authorization Filter
|
+--> Check permissions
|
v
Controller
The important idea is that the JWT filter does not normally call the controller directly.
Instead, it authenticates the request and then allows the request to continue through the security chain.
401 vs 403
Filters are commonly involved when dealing with HTTP security errors.
401 Unauthorized
A 401 Unauthorized response generally means that authentication is missing or invalid.
Example:
Request
|
v
Authentication Filter
|
X
401 Unauthorized
In simple terms:
401 = "I don't know who you are."
403 Forbidden
A 403 Forbidden response generally means that the user is authenticated but does not have sufficient permissions.
Example:
Request
|
v
Authentication
|
v
Authorization
|
X
403 Forbidden
In simple terms:
403 = "I know who you are, but you are not allowed to do this."
Important Spring Security Filters
Spring Security contains many filters, and the exact chain depends on the configuration.
Some commonly encountered filters include:
SecurityContextHolderFilter
Responsible for making the security context available during request processing.
The security context contains information about the current authentication.
Conceptually:
Request
|
v
SecurityContextHolderFilter
|
v
SecurityContext
|
v
Other Security Filters
BasicAuthenticationFilter
Processes HTTP Basic authentication credentials.
For example:
Authorization: Basic <credentials>
It can extract the credentials and create an authenticated security context when authentication succeeds.
BearerTokenAuthenticationFilter
Processes bearer tokens such as JWTs.
For example:
Authorization: Bearer eyJhbGciOi...
The filter extracts the bearer token and delegates authentication to the appropriate authentication mechanism.
AuthorizationFilter
Responsible for evaluating whether the authenticated request has permission to access the requested resource.
Conceptually:
Authenticated User
|
v
AuthorizationFilter
|
+---- Allowed ------> Controller
|
+---- Not Allowed --> 403
Complete Request Flow
Putting everything together:
Client
|
v
HTTP Request
|
v
Servlet Container
|
v
Servlet Filters
|
v
Spring Security Filter Chain
|
+--> Security Context
|
+--> Authentication
|
+--> JWT / Basic Authentication
|
+--> Authorization
|
v
DispatcherServlet
|
v
Spring MVC Interceptors
|
v
Controller
|
v
Service
|
v
Repository
|
v
Database
|
v
HTTP Response
|
v
Filters
|
v
Client
This illustrates an important distinction:
Servlet Filter
|
| operates at HTTP / Servlet level
v
Spring Security Filter
|
| handles security concerns
v
Spring MVC Interceptor
|
| handles MVC/controller concerns
v
Controller
Key Takeaways
1. What is a Filter?
A Filter intercepts HTTP requests and responses before and/or after the request reaches the controller.
2. What is a Filter Chain?
A Filter Chain is an ordered sequence of filters through which a request passes.
The key method is:
filterChain.doFilter(request, response);
Calling it allows the request to continue.
3. How do you create a custom filter?
A common approach in Spring Boot is:
public class MyFilter extends OncePerRequestFilter {
...
}
4. Can a Filter block a request?
Yes.
A filter can return without calling filterChain.doFilter().
For example:
if (invalidRequest) {
response.setStatus(401);
return;
}
5. What is the Spring Security Filter Chain?
It is a specialized chain of filters responsible for security-related processing such as:
- Authentication
- Authorization
- Security context
- JWT/Bearer tokens
- HTTP Basic authentication
- CSRF
- Exception handling
6. Authentication vs Authorization
Remember:
Authentication
= Who are you?
Authorization
= What are you allowed to do?
7. 401 vs 403
Remember:
401
= Authentication problem
403
= Authorization problem
8. The Big Picture
The most important concept to remember is:
HTTP Request
|
v
Filter Chain
|
v
Spring Security Filters
|
v
Authentication
|
v
Authorization
|
v
Spring MVC
|
v
Controller
Understanding Filters and the Filter Chain makes it much easier to understand how Spring Security, JWT authentication, authentication, authorization, SecurityContext, 401, 403, and custom security filters work internally.