PrepZone Logo
PrepZone

Filters vs Interceptors vs Security Chain

The request pipeline from servlet filter through interceptor to controller.

Why this matters

  • Placing logic in the wrong layer (security in a controller, logging in a filter) creates maintenance nightmares.
  • Filters run before Spring MVC; interceptors run after handler mapping — knowing the order prevents duplicate processing.
  • The middleware stack is where correlation IDs, request logging, and authentication happen.

The request pipeline

Servlet FilterEncoding, CORS
Security Filter ChainAuth, CSRF
DispatcherServlet
HandlerInterceptorPre/post handle
Controller
Filters run before DispatcherServlet. Interceptors wrap the controller. Security filters sit in their own chain.

Layers from outer to inner

  • Servlet Filter — Servlet spec; runs before Spring. Compression, CORS headers, request logging.
  • Security Filter Chain — Spring Security; authentication and authorization.
  • DispatcherServlet — Spring MVC entry point; maps URL to handler.
  • HandlerInterceptor — Pre/post/after-completion around controller execution.
  • Controller — Your BookStore endpoint method.

Servlet filter

Java
@Component
public class CorrelationIdFilter extends OncePerRequestFilter {
    private static final String CORRELATION_HEADER = "X-Correlation-Id";

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain chain)
            throws ServletException, IOException {
        String correlationId = request.getHeader(CORRELATION_HEADER);
        if (correlationId == null) {
            correlationId = UUID.randomUUID().toString();
        }
        MDC.put("correlationId", correlationId);
        response.setHeader(CORRELATION_HEADER, correlationId);
        try {
            chain.doFilter(request, response);
        } finally {
            MDC.remove("correlationId");
        }
    }
}

OncePerRequestFilter guarantees one execution per request, even with forwards.

Handler interceptor

Java
@Component
public class RequestTimingInterceptor implements HandlerInterceptor {
    private static final String START_TIME = "startTime";

    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) {
        request.setAttribute(START_TIME, System.currentTimeMillis());
        return true;  // continue processing
    }

    @Override
    public void afterCompletion(HttpServletRequest request,
                                HttpServletResponse response,
                                Object handler, Exception ex) {
        long start = (long) request.getAttribute(START_TIME);
        long elapsed = System.currentTimeMillis() - start;
        log.info("{} {} completed in {}ms",
            request.getMethod(), request.getRequestURI(), elapsed);
    }
}

Register the interceptor:

Java
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new RequestTimingInterceptor())
            .addPathPatterns("/api/**");
    }
}

Filter vs interceptor

FeatureServlet FilterHandlerInterceptor
SpecServlet (Jakarta)Spring MVC
Runs beforeDispatcherServletHandler mapping
Has access toRaw request/responseHandler method, ModelAndView
Use forEncoding, correlation IDsTiming, auth checks, model attributes
Registered via@Component or FilterRegistrationBeanWebMvcConfigurer.addInterceptors()
  • Spec

    Servlet FilterServlet (Jakarta)
    HandlerInterceptorSpring MVC
  • Runs before

    Servlet FilterDispatcherServlet
    HandlerInterceptorHandler mapping
  • Has access to

    Servlet FilterRaw request/response
    HandlerInterceptorHandler method, ModelAndView
  • Use for

    Servlet FilterEncoding, correlation IDs
    HandlerInterceptorTiming, auth checks, model attributes
  • Registered via

    Servlet Filter@Component or FilterRegistrationBean
    HandlerInterceptorWebMvcConfigurer.addInterceptors()

Filter ordering

When multiple filters exist, order matters:

Java
@Configuration
public class FilterConfig {
    @Bean
    public FilterRegistrationBean<CorrelationIdFilter> correlationFilter() {
        FilterRegistrationBean<CorrelationIdFilter> bean = new FilterRegistrationBean<>();
        bean.setFilter(new CorrelationIdFilter());
        bean.addUrlPatterns("/api/*");
        bean.setOrder(1);  // runs first
        return bean;
    }
}

Spring Security filters have their own ordered chain (order ~-100). Custom filters typically run before or after security depending on purpose.

Security filter chain position

Spring Security installs ~15 filters in a specific order:

Default security filter order

  1. SecurityContextPersistenceFilter — Load/save security context
  2. UsernamePasswordAuthenticationFilter — Form login
  3. BearerTokenAuthenticationFilter — JWT validation
  4. AuthorizationFilter — Access decision
  5. ExceptionTranslationFilter — Convert auth exceptions to HTTP responses

Custom security filters slot into this chain via SecurityFilterChain configuration — covered in the security module.

@ControllerAdvice is not middleware

@ControllerAdvice handles exceptions and model attributes after the controller runs (or instead of it). It is not part of the inbound filter/interceptor chain — do not use it for request preprocessing.

Practical BookStore stack

ConcernLayerImplementation
Correlation IDServlet FilterCorrelationIdFilter
AuthenticationSecurity FilterJWT bearer token filter
Request timingInterceptorRequestTimingInterceptor
Exception mappingControllerAdviceBookStoreExceptionHandler
Audit loggingAOP Aspect@Audited annotation
  • Correlation ID

    LayerServlet Filter
    ImplementationCorrelationIdFilter
  • Authentication

    LayerSecurity Filter
    ImplementationJWT bearer token filter
  • Request timing

    LayerInterceptor
    ImplementationRequestTimingInterceptor
  • Exception mapping

    LayerControllerAdvice
    ImplementationBookStoreExceptionHandler
  • Audit logging

    LayerAOP Aspect
    Implementation@Audited annotation

Quick recall

Everything you need if you only revisit this box.

  • Request flow: Filter → Security Chain → DispatcherServlet → Interceptor → Controller.
  • Servlet filters operate on raw HTTP; interceptors operate on mapped handler methods.
  • OncePerRequestFilter prevents duplicate filter execution on forwards.
  • Register interceptors via WebMvcConfigurer.addInterceptors().
  • Use filters for universal concerns (correlation IDs); interceptors for MVC-specific ones (timing).
  • @ControllerAdvice handles exceptions, not inbound request preprocessing.

Test yourself

Answer these before moving on — recall is what makes it stick.