Kotlin, Spring Boot : HandlerInterceptor vs Filter

Romman SabbirRomman Sabbir
3 min read

In Spring Boot, both Filters and HandlerInterceptors help us intercept HTTP requests and responses, but they work at different levels in the app. Picking the right one depends on what you need—whether it's low-level request handling with a Filter or more Spring-aware processing with a HandlerInterceptor.

In this article, we'll explore:

  1. Key Differences between HandlerInterceptor and Filter

  2. When to Use Each

  3. Code Examples (Including JNDI Injection Prevention at the Filter Layer)

  4. Best Practices

HandlerInterceptor vs Filter: Core Differences

FeatureHandlerInterceptorFilter
LayerSpring MVC (after DispatcherServlet)Servlet (before DispatcherServlet)
Spring ContextFull DI support❌ No DI (unless manually bridged)
Access toController metadata (e.g., @RequestMapping)Raw ServletRequest/ServletResponse
Execution OrderAfter routing, before controller logicBefore Spring processes the request
Modify ResponseLimited (cannot modify body easily)Full control (via ServletResponse)
Use Cases- Auth checks based on annotations- Logging
- Request/response logging- Request/response modification
- Adding global model attributes- Security (CORS, JNDI protection)

When to Use Which?

Use HandlerInterceptor When You Need:

  • Spring Dependency Injection (e.g., @Autowired services)

  • Access to Controller Metadata (e.g., method annotations)

  • Pre/Post-Processing Around Controllers (e.g., logging execution time)

Use Filter When You Need:

  • Low-Level Request/Response Manipulation (e.g., modifying headers)

  • Block Requests Before Spring Processes Them (e.g., security checks)

  • Servlet-Specific Features (e.g., HttpServletRequest wrappers)

Code Examples

Example 1: HandlerInterceptor (Spring-Aware)

@Component
class AuthInterceptor(
    private val authService: AuthService // DI works
) : HandlerInterceptor {

    override fun preHandle(
        request: HttpServletRequest,
        response: HttpServletResponse,
        handler: Any
    ): Boolean {
        if (!authService.isValidToken(request.getHeader("X-Auth-Token"))) {
            response.status = HttpStatus.UNAUTHORIZED.value()
            return false
        }
        return true
    }
}

// Registration
@Configuration
class WebConfig : WebMvcConfigurer {
    override fun addInterceptors(registry: InterceptorRegistry) {
        registry.addInterceptor(AuthInterceptor())
    }
}

Example 2: Filter (Servlet-Level)

Preventing JNDI Injection (Security Filter)

@Component
@Order(1) // High priority for security
class JndiInjectionFilter : Filter {

    override fun doFilter(
        request: ServletRequest,
        response: ServletResponse,
        chain: FilterChain
    ) {
        val httpRequest = request as HttpServletRequest

        // Block JNDI lookup attempts (e.g., Log4Shell)
        if (httpRequest.queryString?.lowercase()?.contains("jndi:") == true) {
            (response as HttpServletResponse).sendError(
                HttpStatus.FORBIDDEN.value(),
                "JNDI lookup blocked"
            )
            return
        }

        chain.doFilter(request, response)
    }
}

Why This Works:

  • Filters run before Spring processes the request.

  • Blocks malicious jndi: patterns (e.g., Log4Shell exploits).

  • Does not rely on Spring (works at the servlet level).

Best Practices

Use Filters For:

  • Security (e.g., JNDI, XSS, SQLi filters)

  • Infrastructure (e.g., logging, compression, CORS)

  • Request Wrapping (e.g., caching, modifying headers)

Use HandlerInterceptors For:

  • Business Logic (e.g., role-based auth checks)

  • Controller-Specific Logic (e.g., @PreAuthorize-like checks)

  • Post-Processing (e.g., adding response headers after execution)

Avoid:

  • Using Filters for Spring-specific tasks.

  • Using Interceptors for raw request/response modification.

Conclusion

AspectFilterHandlerInterceptor
Best ForSecurity, logging, raw manipulationBusiness logic, Spring integration
Execution PointBefore SpringAfter routing, before controller
Spring DI SupportNoYes
Use Case ExampleJNDI injection blockingRole-based auth checks

Final Recommendation

  • For security (e.g., JNDI, CORS) → Use Filter.

  • For Spring-aware logic (e.g., auth, logging) → Use HandlerInterceptor.


That’s it for today. Happy coding…

0
Subscribe to my newsletter

Read articles from Romman Sabbir directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Romman Sabbir
Romman Sabbir

Senior Android Engineer from Bangladesh. Love to contribute in Open-Source. Indie Music Producer.