skip to content

Spring Security Test Support

The security test module seeds an authenticated context with annotations and attaches users, JWTs or CSRF tokens to individual requests. Interviewers ask how you prove an endpoint is actually protected, and this is the toolkit.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

questions

5

How do you run an MVC slice/integration test as an authenticated user without performing a real login? Explain @WithMockUser.

level: juniorimportance: must knowfreq 80%

answer

  1. seeds SecurityContext before test, clears after
  2. default user/ROLE_USER, roles auto-prefix ROLE_
  3. WithSecurityContextTestExecutionListener
  4. no real auth, no filter chain
  5. still need .with(csrf()) for POST

basics

~20 s

Annotate the test method (or class) with @WithMockUser from spring-security-test. Before the test runs it seeds the SecurityContext with a fake authenticated user (default name 'user', role 'ROLE_USER'), so security checks pass without a real login.

solid answer

~40 s

@WithMockUser (in spring-security-test) populates the SecurityContextHolder with a UsernamePasswordAuthenticationToken before the test body executes, and clears it afterward. Attributes let you customize identity: username/value for the name, roles=... which auto-prefix ROLE_, or authorities=... for raw, unprefixed authorities (you can't use both). Because it runs via a JUnit test-execution listener (WithSecurityContextTestExecutionListener, auto-registered by @SpringBootTest / spring-security's test setup), no password check, no filter chain, no HTTP round-trip happens. It works for any test that reads the SecurityContext — including MockMvc method-security tests. Pair it with .with(csrf()) on mutating MockMvc requests since it doesn't add a CSRF token. Put it on the class to apply to every method, or on a method to override.

code

java · 19 lines
java
@WebMvcTest(AccountController.class)
class AccountControllerTest {

  @Autowired MockMvc mvc;

  @Test
  @WithMockUser // name "user", authority ROLE_USER
  void getProfile_authenticated_returns200() throws Exception {
    mvc.perform(get("/api/me"))
       .andExpect(status().isOk());
  }

  @Test
  @WithMockUser(username = "admin", roles = {"ADMIN"}) // ROLE_ADMIN
  void deleteUser_asAdmin_ok() throws Exception {
    mvc.perform(delete("/api/users/42").with(csrf())) // CSRF still required
       .andExpect(status().isNoContent());
  }
}

go deeper

for a junior

Know it makes the test run as a logged-in user and roles get ROLE_ prefixed; remember csrf() for POST.

for a middle

Explain the listener mechanism, roles-vs-authorities, class-vs-method placement, and that no real login happens.

for a senior

Contrast with @WithUserDetails/@WithSecurityContext for custom principals; know setupBefore and when it isn't the right tool (testing the actual login path).

for a principal

Reason about composed meta-annotations for reusable identities and the boundary between context-seeding tests and full filter-chain/authentication-flow tests.

## What it is `@WithMockUser` is an annotation from the **spring-security-test** module (`org.springframework.security.test.context.support.WithMockUser`). It sets up a fully-populated `SecurityContext` *before* your test method body runs, so any code that calls `SecurityContextHolder.getContext().getAuthentication()` — or any authorization rule like `@PreAuthorize`, `.authenticated()`, method security — sees a logged-in principal. It removes the context after the test. ## The mechanism Spring Security registers a `WithSecurityContextTestExecutionListener` (a JUnit `TestExecutionListener`). When it sees a `@WithMockUser` (or any `@WithSecurityContext`-meta-annotated annotation) on the test method or class, it builds a `SecurityContext` containing a `UsernamePasswordAuthenticationToken` and stores it in the `SecurityContextHolder` before the test, then clears it after. This listener is auto-registered when you use `@SpringBootTest`, `@WebMvcTest`, or otherwise import Spring's test context — you do **not** wire it manually. Key point: **no real authentication happens.** There is no password verification, no `AuthenticationManager` call, no HTTP request, no security filter chain. It's a shortcut that directly seeds the context. ## Attributes - `value` / `username` — the principal name. Default is `"user"`. - `password` — default `"password"` (rarely matters). - `roles` — e.g. `roles = {"ADMIN"}` becomes authority `ROLE_ADMIN`. Default is `{"USER"}` → `ROLE_USER`. **Spring auto-prefixes `ROLE_`; if you write `roles = "ROLE_ADMIN"` you get the illegal `ROLE_ROLE_ADMIN` and an exception.** - `authorities` — raw authorities with **no** prefix, e.g. `authorities = {"read", "ROLE_ADMIN"}`. You may set `roles` OR `authorities`, never both (throws). ## Scope / placement - On a **method** → applies to that test only. - On the **class** → applies to every test method (each can override with its own annotation). - On a **custom composed annotation** (meta-annotation) → build reusable `@WithMockAdmin`. ## Common gotchas 1. **CSRF**: `@WithMockUser` authenticates you but does NOT add a CSRF token. A `POST`/`PUT`/`DELETE` through MockMvc against a default (CSRF-enabled) config returns 403 unless you add `.with(csrf())`. 2. **roles vs authorities prefix confusion** — see above; the #1 mistake. 3. **It seeds the context, not an HTTP session** — good for method security and controllers alike, but it isn't testing your real login/filter path. To test the actual authentication flow you need a real request (e.g. `formLogin()` or `httpBasic()` post-processors), not `@WithMockUser`. 4. **Principal type** — the principal is a `User`/String, not your custom `UserDetails`. If your controller casts to a custom principal type, use `@WithUserDetails` or `user(...).password(...)` / a custom `@WithSecurityContext` instead. 5. **setupBefore** — `@WithMockUser(setupBefore = TestExecutionEvent.TEST_EXECUTION)` seeds the context *after* `@Before`/`@BeforeEach` methods instead of before them (the default is `TEST_METHOD`, i.e. before setup). Useful when setup code itself needs the user, or must NOT. ## When to use Default choice for controller/method-security tests where you just need *some* authenticated user with given roles and don't care about the exact `UserDetails` instance or a real login round-trip.

  • Why does a POST in a @WithMockUser test return 403 even though the user is authenticated?
    @WithMockUser only seeds authentication; it does not add a CSRF token. With Spring Security's default CSRF protection, mutating requests need a valid token, so you must add SecurityMockMvcRequestPostProcessors.csrf() (e.g. .with(csrf())).
  • What's the difference between roles = {"ADMIN"} and authorities = {"ADMIN"}?
    roles auto-prefixes ROLE_, giving authority ROLE_ADMIN (matches hasRole('ADMIN')). authorities is taken verbatim with no prefix, giving authority ADMIN (matches hasAuthority('ADMIN')). You can set one or the other, not both.

context

open as a page

What are SecurityMockMvcRequestPostProcessors (user(), jwt(), csrf()) and how do they differ from @WithMockUser?

level: middleimportance: must knowfreq 70%

basics

~20 s

They are per-request modifiers you attach with MockMvc's .with(...). user(...) sets an authenticated principal on that request, jwt(...) sets a JWT-based authentication for resource-server tests, and csrf() adds a valid CSRF token. Unlike @WithMockUser they apply to a single request, imperatively.

open as a page

How do you authenticate a WebTestClient request in a reactive (WebFlux) test using spring-security-test?

level: seniorimportance: should knowfreq 45%

basics

~10 s

Use WebTestClient's mutateWith(...) with mutators from SecurityMockServerConfigurers, e.g. client.mutateWith(mockUser("alice").roles("ADMIN")). There are also mockJwt(), mockOpaqueToken(), and csrf() mutators. In WebFlux, authentication lives in the Reactor context, so these configure the exchange accordingly.

open as a page

Compare @WithMockUser, @WithUserDetails, and @WithSecurityContext. When do you need each?

level: seniorimportance: should knowfreq 55%

basics

~20 s

@WithMockUser fabricates a simple principal from annotation attributes. @WithUserDetails loads a real principal via your UserDetailsService bean by username, so the principal is your actual UserDetails type. @WithSecurityContext is the extensible base that lets you build a fully custom SecurityContext/Authentication via a factory.

open as a page

A team's MockMvc POST tests intermittently fail with 403 despite @WithMockUser, and their 'anonymous access returns 401' test passes even after they broke config. What test-support pitfalls explain this?

level: principalimportance: should knowfreq 35%

basics

~20 s

The 403 comes from missing CSRF tokens on mutating requests — @WithMockUser authenticates but doesn't add one; they must use .with(csrf()). The false-passing test likely tests without wiring Spring Security into MockMvc (no apply(springSecurity())) or a @WebMvcTest with mocked/absent filter chain, so security isn't actually enforced.

open as a page