Explain how AnonymousAuthenticationFilter works: where it sits in the chain, what it inserts, and the role of its key.
answer
- Late in chain: after real auth, before AuthorizationFilter
- Acts only if Authentication == null
- key -> keyHash validated by AnonymousAuthenticationProvider
- populates, never rejects
- Boot auto-generates the key
basics
~20 sNear the end of the filter chain, AnonymousAuthenticationFilter checks if the SecurityContext already has an Authentication. If not, it builds an AnonymousAuthenticationToken (default principal anonymousUser, ROLE_ANONYMOUS) using a configured key and stores it. The key lets AnonymousAuthenticationProvider verify tokens it created.
solid answer
~50 sAnonymousAuthenticationFilter is positioned late in the security filter chain — after all real authentication filters (session, JWT, form login, etc.) but before authorization filters like AuthorizationFilter. On each request it reads SecurityContextHolder; if getAuthentication() is null, it constructs an AnonymousAuthenticationToken and stores it in the context. The token carries a principal (default 'anonymousUser'), authorities (default ROLE_ANONYMOUS), and a key. That key is a shared secret hashed into the token; AnonymousAuthenticationProvider validates that any anonymous token presented for authentication has the same key hash, preventing a forged anonymous token from a different origin being trusted. Because it only acts when the context is empty, a genuinely authenticated request is untouched. The filter guarantees the downstream contract that Authentication is never null, enabling uniform authorization decisions and SpEL like isAnonymous(). Ordering matters: placing it too early would shadow real authentication.
code
java · 9 lines@Bean
SecurityFilterChain chain(HttpSecurity http) throws Exception {
http.anonymous(anon -> anon
.key("katajob-anon-key") // hashed into the token; verified by the provider
.principal("guest")
.authorities("ROLE_GUEST"))
.authorizeHttpRequests(a -> a.anyRequest().authenticated());
return http.build();
}go deeper
Know a filter inserts a placeholder token when nobody is logged in.
Know defaults (anonymousUser / ROLE_ANONYMOUS) and that it only acts when the context is empty.
Explain chain ordering rationale, the key/keyHash and AnonymousAuthenticationProvider, and populate-not-reject.
Discuss configuration/disable trade-offs, audit implications of custom principal, and ordering pitfalls in custom chains.
**Position in the chain.** The Spring Security filter chain runs filters in a fixed order. `AnonymousAuthenticationFilter` is deliberately near the end — *after* filters that establish real identity (e.g., `SecurityContextHolderFilter`/`SecurityContextPersistenceFilter`, `UsernamePasswordAuthenticationFilter`, `BearerTokenAuthenticationFilter`, `RememberMeAuthenticationFilter`) and *before* the `AuthorizationFilter` (formerly `FilterSecurityInterceptor`). This ordering means: give real authentication every chance first; only if nobody authenticated do we fall back to anonymous; then authorization sees a guaranteed-non-null token. **What it does per request.** ``` if (SecurityContextHolder.getContext().getAuthentication() == null) { context.setAuthentication(createAuthentication(request)); } ``` `createAuthentication` builds an `AnonymousAuthenticationToken(key, principal, authorities)`. Defaults: principal = `"anonymousUser"`, authorities = `ROLE_ANONYMOUS`. If a token already exists (real user, or already anonymous from an earlier point), it does nothing. **The `key`.** The constructor requires a `key` string. Internally the token stores `keyHash = key.hashCode()`. The paired `AnonymousAuthenticationProvider` is also configured with a key and, during authentication, checks `provider.key.hashCode() == token.getKeyHash()`; a mismatch throws `BadCredentialsException`. Purpose: ensure an `AnonymousAuthenticationToken` is only trusted if it was minted by *this* application's filter, not injected/forged from elsewhere. In Boot, the key is auto-generated (a random UUID) unless you set one. **Why authenticated=true.** The token reports `isAuthenticated()==true` so it is a valid `Authentication`, but `AuthenticationTrustResolverImpl.isAnonymous()` still recognizes its type, so authorization treats it as unauthenticated. **Configuration.** ```java http.anonymous(anon -> anon .key("myKey") .principal("guest") .authorities("ROLE_GUEST")); // or disable entirely http.anonymous(AbstractHttpConfigurer::disable); ``` Disabling means unauthenticated requests keep a `null` Authentication. **Gotchas.** - Ordering: never move it ahead of real auth filters, or every request becomes anonymous and real logins get shadowed. - The filter does not *reject* anything; it only *populates*. Rejection is the authorization layer's job. - Custom principal/authorities propagate everywhere `getAuthentication()` is read — audit logs may then show 'guest' etc.
- Why does the filter sit near the END of the chain rather than the start?So real authentication filters run first. Anonymous is a fallback: only when no filter established an identity does it insert a placeholder. Placing it early would shadow real logins by filling the context before they run.
- What is the key for, and what breaks if the filter's key and the provider's key differ?The key is hashed into the token so AnonymousAuthenticationProvider can verify the token originated from this app. If they differ, the provider throws BadCredentialsException when validating an anonymous token.
- Does AnonymousAuthenticationFilter ever reject a request?No. It only populates the context. Access decisions (allow/deny) are made later by the AuthorizationFilter / authorization rules.
saying these in an interview costs you the question
- Saying the filter runs first / before real authentication
- Claiming the filter rejects unauthorized requests
- Not knowing the key exists or thinking it is the user's password
- Thinking it overwrites an existing (real) Authentication