skip to content

What is anonymous authentication in Spring Security, and what does the SecurityContext hold for an unauthenticated request?

level: juniorimportance: should knowfreq 45%

answer

  1. Placeholder token, not null
  2. AnonymousAuthenticationFilter at chain end
  3. principal='anonymousUser', ROLE_ANONYMOUS
  4. authenticated=true but TrustResolver says anonymous

basics

~20 s

For a request with no login, Spring Security still puts a placeholder 'anonymous' user into the SecurityContext instead of leaving it empty. So code can always assume an Authentication object exists rather than handling null.

solid answer

~40 s

Spring Security never leaves the SecurityContext empty for a request that reached the filter chain without authenticating. The AnonymousAuthenticationFilter, near the end of the chain, checks whether the SecurityContextHolder already holds an Authentication; if not, it inserts an AnonymousAuthenticationToken. That token has a principal (default string 'anonymousUser'), one authority (default ROLE_ANONYMOUS), and is marked authenticated=true, so downstream code can uniformly call getAuthentication() without null checks. It represents 'this is not a logged-in user' rather than 'no information at all'. This is why an unauthenticated request still has a non-null Authentication and why rules such as isAnonymous() work. Crucially, AuthenticationTrustResolver still classifies anonymous as unauthenticated, so anything requiring real authentication is denied and triggers the AuthenticationEntryPoint.

code

java · 6 lines
java
// Downstream code can rely on a non-null Authentication
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
// For an unauthenticated request this is an AnonymousAuthenticationToken:
//   auth.getName()            -> "anonymousUser"
//   auth.getAuthorities()     -> [ROLE_ANONYMOUS]
//   auth.isAuthenticated()    -> true (but trust resolver treats it as anonymous)

go deeper

for a junior

Know that unauthenticated requests still get a placeholder Authentication instead of null.

for a middle

Explain the token defaults (anonymousUser, ROLE_ANONYMOUS) and that the filter sits at the chain end.

for a senior

Explain why authenticated=true yet still treated as unauthenticated via AuthenticationTrustResolver.

for a principal

Discuss enabling/disabling trade-offs, null-handling contracts, and how it interacts with entry points and stateless APIs.

**The problem it solves.** Every request flows through the Spring Security filter chain, and application code reads the current user via `SecurityContextHolder.getContext().getAuthentication()`. If unauthenticated requests left this `null`, every consumer would need a null check. Anonymous authentication removes that special case: even a request with no credentials gets a *placeholder* `Authentication`. **Key classes.** - `AnonymousAuthenticationFilter` — sits near the end of the chain (after the real authentication filters). If the context still has no `Authentication`, it creates and stores an anonymous token. - `AnonymousAuthenticationToken` — the placeholder token. Constructed with a `key`, a `principal` (default `"anonymousUser"`), and authorities (default `ROLE_ANONYMOUS`). Its `isAuthenticated()` returns `true`. - `AuthenticationTrustResolver` (default `AuthenticationTrustResolverImpl`) — the thing that decides whether an `Authentication` is 'really' authenticated. `isAnonymous()` returns true when the token is an `AnonymousAuthenticationToken`, so authorization treats anonymous as *not* logged in. **Why authenticated=true but still 'unauthenticated'.** The `authenticated` flag is `true` only so the token is a well-formed, usable `Authentication`. Whether a rule like `authenticated()` passes is decided by the trust resolver, not by the flag — and the resolver reports anonymous tokens as anonymous. So `.authenticated()` and `@PreAuthorize("isAuthenticated()")` both reject anonymous requests. **In Boot.** With Spring Boot's default security, anonymous is enabled automatically; you rarely configure it explicitly. You only touch it to change the key/principal/authorities or to disable it (`http.anonymous(AbstractHttpConfigurer::disable)`), which makes the context hold `null` again for unauthenticated requests. **When it matters.** Method-level SpEL such as `isAnonymous()`, audit logging that reads the principal, and code that always expects a non-null `Authentication`.

  • If anonymous auth marks the token authenticated=true, why does an endpoint secured with authenticated() still reject an anonymous request?
    Because access decisions use AuthenticationTrustResolver, not the boolean flag. The resolver reports AnonymousAuthenticationToken as anonymous, so authenticated() / isAuthenticated() fail and the AuthenticationEntryPoint is triggered.
  • What happens to getAuthentication() if you disable anonymous authentication?
    For unauthenticated requests the SecurityContext holds null again, so consumers must null-check. AnonymousAuthenticationFilter no longer runs to insert a placeholder.

saying these in an interview costs you the question

  • Saying the SecurityContext is null for unauthenticated requests when anonymous is enabled
  • Claiming authenticated=true means the anonymous user passes authenticated() checks
  • Confusing anonymous (a placeholder identity) with no filter chain at all

context