skip to content

What is the ExceptionTranslationFilter and what job does it do in the Spring Security filter chain?

level: juniorimportance: must knowfreq 55%

answer

  1. try/catch around downstream chain
  2. only AuthenticationException + AccessDeniedException
  3. EntryPoint = start auth; AccessDeniedHandler = 403
  4. everything else rethrown
  5. sits before AuthorizationFilter

basics

~20 s

It's a filter that catches two security exceptions thrown further down the chain — AuthenticationException (not logged in) and AccessDeniedException (logged in but not allowed) — and turns them into an HTTP response instead of an error page.

solid answer

~40 s

ExceptionTranslationFilter sits near the end of the Spring Security filter chain and wraps the filters after it in a try/catch. It only reacts to two exceptions: AuthenticationException (the user isn't authenticated) and AccessDeniedException (the user is authenticated but lacks permission). For an AuthenticationException it kicks off authentication by calling the configured AuthenticationEntryPoint (e.g. redirect to /login or send 401). For an AccessDeniedException from a real user it hands off to the AccessDeniedHandler (403). Any other exception it simply rethrows. Its purpose is to translate Java security exceptions into a meaningful HTTP response — a login prompt or a forbidden result — rather than letting them bubble up as a 500 error.

code

java · 20 lines
java
// Conceptually what ExceptionTranslationFilter.doFilter does:
try {
    chain.doFilter(request, response); // AuthorizationFilter runs downstream
} catch (IOException ex) {
    throw ex;
} catch (Exception ex) {
    // Unwrap the cause chain looking for the two security exceptions
    AuthenticationException authEx =
        throwableAnalyzer.getFirstThrowableOfType(AuthenticationException.class, causeChain);
    AccessDeniedException accessEx =
        throwableAnalyzer.getFirstThrowableOfType(AccessDeniedException.class, causeChain);

    if (authEx != null) {
        sendStartAuthentication(request, response, chain, authEx); // -> entryPoint.commence
    } else if (accessEx != null) {
        handleAccessDeniedException(request, response, chain, accessEx); // entryPoint OR handler
    } else {
        rethrow(ex); // not a security exception -> propagate (500)
    }
}

go deeper

for a junior

Know the one-line purpose: catches auth/access-denied exceptions and turns them into a login prompt or 403.

for a middle

Distinguish the two exceptions and the two responses (entry point vs access-denied handler); know it only wraps downstream filters.

for a senior

Explain placement before AuthorizationFilter, the rethrow of non-security exceptions, and that it doesn't handle login-filter failures.

for a principal

Frame it as the boundary where domain security exceptions become HTTP protocol responses, and reason about ordering guarantees it depends on.

## What it is `ExceptionTranslationFilter` is one of the built-in filters in Spring Security's filter chain (`SecurityFilterChain`). A *filter chain* is an ordered list of servlet filters that every HTTP request passes through before reaching your controller. Each filter can do work before and after the rest of the chain runs. `ExceptionTranslationFilter`'s single responsibility is **error translation**: it converts two specific Java exceptions into an appropriate HTTP outcome. It does **not** make authorization decisions itself. ## How it works mechanically It wraps the *downstream* part of the chain in a try/catch: ``` try { chain.doFilter(request, response); // run the rest of the chain } catch (Exception ex) { // look for AuthenticationException or AccessDeniedException inside ex } ``` Because it only wraps filters that come **after** it, it can only handle exceptions thrown downstream. In modern Spring Security 6 the filter that actually throws `AccessDeniedException` is `AuthorizationFilter` (it was `FilterSecurityInterceptor` in older versions), which is placed *after* `ExceptionTranslationFilter` on purpose. The two exceptions it recognizes: - **`AuthenticationException`** — the principal is not authenticated (anonymous, or credentials were rejected downstream). Response: start authentication via `AuthenticationEntryPoint.commence(...)`. Typical results: a `302` redirect to a login page, or a `401 Unauthorized`. - **`AccessDeniedException`** — the principal *is* authenticated but is not permitted. Response: for a fully authenticated user, delegate to `AccessDeniedHandler` (usually a `403 Forbidden`). Anything that is **not** one of these two (or doesn't unwrap to one of them) is **rethrown** unchanged, so it becomes a normal servlet error (often a `500`). ## Why it matters Without this filter, a security exception thrown deep in the chain would surface as an unhandled servlet error. `ExceptionTranslationFilter` is what makes the difference between a user seeing a login screen and seeing a stack trace. ## Key terms - **`AuthenticationEntryPoint`** — a strategy object that *commences* (starts) the authentication process. Called on `AuthenticationException`. - **`AccessDeniedHandler`** — a strategy object that decides what to do when an authenticated user is forbidden. Called on `AccessDeniedException` for real users. ## Gotcha It does **not** handle failures from authentication filters like `UsernamePasswordAuthenticationFilter` — those handle their own success/failure via `AuthenticationSuccessHandler`/`AuthenticationFailureHandler`. `ExceptionTranslationFilter` mostly reacts to the authorization step at the end of the chain.

  • Which downstream filter typically throws the AccessDeniedException that this filter catches?
    AuthorizationFilter in Spring Security 6 (formerly FilterSecurityInterceptor). It is placed after ExceptionTranslationFilter so the try/catch can wrap it.
  • What happens to an exception that is neither AuthenticationException nor AccessDeniedException?
    It is rethrown unchanged and propagates as a normal servlet error, typically surfacing as a 500 handled elsewhere.

saying these in an interview costs you the question

  • Claiming it catches every exception in the chain (it only handles the two security exceptions)
  • Saying it handles login-form authentication failures (those go through AuthenticationFailureHandler)
  • Confusing it with a filter that makes authorization decisions — it only translates exceptions

context