skip to content

What is @WithAnonymousUser for, and how does annotation precedence work at class vs method level?

level: middleimportance: should knowfreq 40%

answer

  1. AnonymousAuthenticationToken / ROLE_ANONYMOUS
  2. principal = 'anonymousUser'
  3. overrides class-level @WithMockUser on one method
  4. method-over-class precedence
  5. faithful 'not logged in' vs empty context

basics

~10 s

@WithAnonymousUser runs a test as an unauthenticated (anonymous) user. It's mainly used to override a class-level @WithMockUser on one specific method that should test the not-logged-in case.

solid answer

~30 s

@WithAnonymousUser populates the SecurityContext with an AnonymousAuthenticationToken (principal 'anonymousUser', authority ROLE_ANONYMOUS) — the state Spring Security uses for requests with no real authentication. Its main value is precedence: when a whole test class is annotated @WithMockUser, a single method annotated @WithAnonymousUser overrides it, letting you assert that unauthenticated access is redirected to login or returns 401/403. Method-level security annotations always win over class-level ones. This is cleaner than manually clearing the SecurityContext and mirrors how anonymous auth actually appears at runtime, so hasRole/isAnonymous() and access rules behave exactly as in production.

code

java · 20 lines
java
@WebMvcTest(ProfileController.class)
@WithMockUser // every test authenticated by default
class ProfileControllerTest {

    @Autowired MockMvc mvc;

    @Test
    void authenticatedUserSeesProfile() throws Exception {
        mvc.perform(get("/profile"))
           .andExpect(status().isOk());
    }

    @Test
    @WithAnonymousUser // overrides the class-level @WithMockUser
    void anonymousIsRedirectedToLogin() throws Exception {
        mvc.perform(get("/profile"))
           .andExpect(status().is3xxRedirection())
           .andExpect(redirectedUrlPattern("**/login"));
    }
}

go deeper

for a junior

Know it runs the test as a not-logged-in user.

for a middle

Explain method-over-class precedence and why it beats clearing the context manually.

for a senior

Connect it to AnonymousAuthenticationFilter/ROLE_ANONYMOUS and correct 401-vs-302 expectations.

for a principal

Use it to systematically cover the unauthenticated branch of authorization matrices in slice tests.

## What 'anonymous' means in Spring Security Spring Security's `AnonymousAuthenticationFilter` gives every request that has *no* authenticated user a placeholder `AnonymousAuthenticationToken` rather than a null `Authentication`. Its principal is the String `"anonymousUser"` and it carries authority `ROLE_ANONYMOUS`. Access rules like `.anonymous()`, SpEL `isAnonymous()`, and `permitAll()` interact with this state. So 'not logged in' at runtime is *not* an empty context — it's an anonymous token. ## What @WithAnonymousUser does `@WithAnonymousUser` (from spring-security-test) sets exactly that state into the test's `SecurityContext` via the same `WithSecurityContextTestExecutionListener` mechanism used by `@WithMockUser`. It reproduces the real 'unauthenticated' condition faithfully — better than clearing the context to null, which doesn't match how the filter chain presents anonymous requests. ## The precedence rule (the real reason it exists) The `@With*` annotations follow **method-over-class** precedence: - Annotate the **class** with `@WithMockUser` so every test runs authenticated. - Annotate **one method** with `@WithAnonymousUser` to flip just that method to unauthenticated. This is the idiomatic way to test the 'logged-out' branch (e.g. a protected endpoint should 302-redirect to `/login`, or an API should return 401/403) inside a class that's otherwise authenticated. Without it you'd manually clear or re-set the context in the test body, which is noisier and easy to forget to undo. ## Gotchas - If a class has `@WithMockUser` and a method has *both* another `@With*` and `@WithAnonymousUser`, that's a configuration error — put exactly one context annotation on the method. - `@WithAnonymousUser` takes no roles/username attributes; anonymous state is fixed. - It only makes sense where the app permits anonymous access to *reach* the security check. If a filter rejects the request earlier, adjust the test expectation accordingly (401 vs 302 depends on your `AuthenticationEntryPoint`). ## When to use - Verifying the unauthenticated path (redirect to login, 401/403) for a specific endpoint within an otherwise-authenticated test class. - Asserting that `permitAll()` / public endpoints are reachable without a user.

  • Why prefer @WithAnonymousUser over just not annotating the method / clearing the SecurityContext?
    A missing annotation may inherit the class-level @WithMockUser, and clearing to null doesn't match runtime, where the AnonymousAuthenticationFilter installs an AnonymousAuthenticationToken. @WithAnonymousUser reproduces the real anonymous state and cleanly overrides the class default for that one method.

saying these in an interview costs you the question

  • Thinking 'unauthenticated' means a null Authentication rather than an AnonymousAuthenticationToken
  • Believing a method with no annotation runs anonymously when the class is @WithMockUser (it inherits the class annotation)
  • Expecting @WithAnonymousUser to accept roles/username attributes

context