In a modern Spring Boot backend, it’s common to intercept HTTP requests for cross-cutting concerns like authentication, authorization, logging, or header injection.
Spring provides two powerful mechanisms to achieve this:
-
Servlet Filters
-
Spring MVC Interceptors
They may seem similar, but they’re designed for different stages of the request lifecycle. In this blog, we’ll explore the differences, real-world examples, and when to use which — backed by best practices.
Quick Comparison: Filter vs Interceptor
| Feature | Filter | Interceptor |
|---|---|---|
| API Level | Servlet (jakarta.servlet.Filter) | Spring MVC (HandlerInterceptor) |
| Execution Point | Before DispatcherServlet | Before/after controller methods |
| Intercepts static resources? | ✅ Yes | ❌ No |
| Access to controller method? | ❌ No | ✅ Yes (via handler) |
| Can block requests? | ✅ Yes (by skipping chain.doFilter()) | ✅ Yes (by returning false in preHandle) |
| Use Cases | CORS, headers, logging, early auth checks | Role-based auth, audit logging, business pre-checks |
Request Lifecycle in Spring Boot
textClient └─> Filter └─> DispatcherServlet └─> Interceptor (preHandle) └─> Controller └─> Interceptor (postHandle) └─> Interceptor (afterCompletion)
When Should You Use a Filter?
Filters are ideal for low-level request manipulation and global request concerns, such as:
-
Logging all traffic (including static resources)
-
Enabling CORS headers
-
Authentication token parsing
-
Charset/encoding setup
Example: Global CORS Filter
java@Component public class CorsFilter implements Filter { @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletResponse response = (HttpServletResponse) res; response.setHeader("Access-Control-Allow-Origin", "*"); response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE"); response.setHeader("Access-Control-Allow-Headers", "Content-Type, X-Auth-Token"); chain.doFilter(req, res); } }
When Should You Use an Interceptor?
Interceptors are perfect for Spring MVC–specific use cases, such as:
-
Role-based access control (RBAC)
-
Request preprocessing based on controller annotations
-
Audit logging
-
Adding attributes to the model
✅ Example: Interceptor for Admin Authorization
java@Component public class AdminInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException { String role = request.getHeader("X-User-Role"); if (!"ADMIN".equalsIgnoreCase(role)) { response.setStatus(HttpServletResponse.SC_FORBIDDEN); response.getWriter().write("Access Denied: Admins only"); return false; } return true; } }
🛠Register in WebMvcConfigurer
java@Configuration public class WebConfig implements WebMvcConfigurer { @Autowired private AdminInterceptor adminInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(adminInterceptor).addPathPatterns("/admin/**"); } }
Real-World Use Case: Combining Filter and Interceptor
Let’s say you’re building a secure API:
-
You validate JWT tokens in every request → ✅ Filter
-
You check user roles after token validation → ✅ Interceptor
Step 1: JWT Filter
java@Component public class JwtTokenFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String token = request.getHeader("Authorization"); if (token == null || !token.startsWith("Bearer ")) { response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); response.getWriter().write("Missing or invalid Authorization header"); return; } // Parse token, extract role (mocked here) String userRole = "ADMIN"; request.setAttribute("userRole", userRole); filterChain.doFilter(request, response); } }
Step 2: Role-Based Interceptor
java@Component public class RoleInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException { String role = (String) request.getAttribute("userRole"); if (!"ADMIN".equals(role)) { response.setStatus(HttpServletResponse.SC_FORBIDDEN); response.getWriter().write("Access denied: ADMINs only"); return false; } return true; } }
Summary: Filter vs Interceptor
| Use Case | Filter | Interceptor |
|---|---|---|
| Logging static and dynamic requests | ✅ Yes | ❌ No |
| Authentication (e.g., token parsing) | ✅ Yes | ❌ Not ideal |
| Authorization (based on role/annotation) | ❌ No | ✅ Yes |
| Modify request before controller | ✅ Yes | ✅ Yes |
| Access handler method/controller details | ❌ No | ✅ Yes |
| Execution order | First | After filter |
Final Thoughts
In most Spring Boot applications, you’ll find yourself using both Filters and Interceptors — not as alternatives but as complements:
-
Use Filters for infrastructure-level concerns (tokens, CORS, headers)
-
Use Interceptors for application-specific logic (RBAC, audit, pre/post handling)
Understanding where each fits in the request lifecycle ensures cleaner architecture, easier debugging, and maintainable code.
0 Comments