skip to content

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

level: middleimportance: must knowfreq 70%

answer

  1. static import, .with(user()/jwt()/csrf())
  2. per-request & imperative vs annotation per-method
  3. jwt() = mock decoded JWT, no signature check
  4. csrf().useInvalidToken() for negative path
  5. SCOPE_ authorities from jwt scopes

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.

solid answer

~40 s

SecurityMockMvcRequestPostProcessors are static factory methods (import statically) producing RequestPostProcessor objects you attach with .with(...) on a MockMvc request. user("alice").roles("ADMIN") builds an authenticated principal for that request; jwt() (from the oauth2 support) seeds a JwtAuthenticationToken so @WebMvcTest resource-server endpoints see a decoded JWT without a real issuer; csrf() injects a valid CSRF token so mutating requests pass Spring's CsrfFilter; anonymous(), httpBasic(), formLogin() cover other cases. Difference from @WithMockUser: post-processors are imperative and per-request (you can vary the user call-by-call in one test and combine several .with()), whereas @WithMockUser is declarative, seeds the SecurityContext once for the whole test method via a TestExecutionListener, and is cleaner when every request in the test shares one identity. They also fit fluent styles better and let you set a fully custom principal object.

code

java · 20 lines
java
import static org.springframework.security.test.web.servlet.request
        .SecurityMockMvcRequestPostProcessors.*;

@Test
void resourceServer_endpoint_withJwtScope() throws Exception {
  mvc.perform(post("/api/orders")
        .with(jwt().jwt(j -> j.subject("alice").claim("scope", "orders:write"))
                   .authorities(new SimpleGrantedAuthority("SCOPE_orders:write")))
        .with(csrf())
        .contentType(APPLICATION_JSON).content("{}"))
     .andExpect(status().isCreated());
}

@Test
void twoUsersInOneTest() throws Exception {
  mvc.perform(get("/api/me").with(user("alice").roles("USER")))
     .andExpect(jsonPath("$.name").value("alice"));
  mvc.perform(get("/api/me").with(user("bob").roles("ADMIN")))
     .andExpect(jsonPath("$.name").value("bob"));
}

go deeper

for a junior

Know .with(csrf()) is needed for POST and .with(user(...)) authenticates one request.

for a middle

Explain per-request vs per-method, jwt() for resource servers, and the roles/authorities/scope prefixing.

for a senior

Discuss testing negative CSRF (useInvalidToken), multiple identities per test, and that jwt() skips signature/issuer validation.

for a principal

Decide test-boundary strategy: seed authentication (jwt/user) for authz tests vs a mock issuer/JwtDecoder for decode-path coverage; keep the two concerns in separate tests.

## What they are `SecurityMockMvcRequestPostProcessors` is a class of **static factory methods** in spring-security-test (`org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors`). Each returns a `RequestPostProcessor` — an object that mutates the `MockHttpServletRequest` right before it's dispatched. You attach them with MockMvc's `.with(...)`: ```java mvc.perform(post("/x").with(user("alice").roles("ADMIN")).with(csrf())) ``` Static-import them: `import static ...SecurityMockMvcRequestPostProcessors.*;` ## The key ones - **`user(String)`** — builds an authenticated principal for *this request only*. Chain `.roles(...)` (ROLE_-prefixed), `.authorities(...)` (raw), `.password(...)`, `.roles`. There is also `user(UserDetails)` to supply your own principal object. - **`jwt()`** — from the OAuth2 resource-server test support. Seeds a `JwtAuthenticationToken` with a mock decoded `Jwt` so an endpoint protected by `oauth2ResourceServer().jwt()` authenticates without a real authorization server or signature. Customize: `jwt().jwt(j -> j.claim("scope","read").subject("alice")).authorities(new SimpleGrantedAuthority("SCOPE_read"))`. Related: `opaqueToken()` for introspection-based resource servers. - **`csrf()`** — adds a valid CSRF token to the request so the `CsrfFilter` accepts POST/PUT/PATCH/DELETE. `csrf().asHeader()` puts it in the `X-CSRF-TOKEN` header instead of a parameter; `csrf().useInvalidToken()` forces a 403 to test the negative path. - **`anonymous()`**, **`httpBasic(user, pass)`**, **`formLogin()`**, **`logout()`** — other flows. ## Difference from @WithMockUser | | `@WithMockUser` (and friends) | RequestPostProcessors | |---|---|---| | Style | Declarative annotation | Imperative `.with(...)` | | Scope | Whole test method (seeds SecurityContext once) | Single request | | Mechanism | `WithSecurityContextTestExecutionListener` before the body | Mutates that `MockHttpServletRequest`/its context | | Vary per request | No (one identity per method) | Yes (different `user(...)` per call) | | OAuth2 JWT | No equivalent annotation | `jwt()` / `opaqueToken()` | Use the annotation when every request in the test shares one identity and you also exercise method-security beans; use post-processors when you need a JWT/opaque-token principal, a custom principal object per request, or several identities in one test. ## Gotchas 1. **`csrf()` is independent of who you are** — you still need it for mutating requests even under `@WithMockUser`. 2. **`jwt()` bypasses signature/issuer** — it does not test your real `JwtDecoder` or JWKS; it tests authorization given a decoded token. To test decoding, use a real/mock issuer. 3. **Authority prefix rules mirror the annotation**: `.roles()` prefixes `ROLE_`; `.authorities()` doesn't; `jwt()` default authorities come from scopes as `SCOPE_...` via `JwtGrantedAuthoritiesConverter` unless you override `.authorities(...)`. 4. **Combining** — you can stack `.with(...)` calls; later ones can override request state. `user(...)` and `@WithMockUser` on the same request: the post-processor wins for that request's authentication. 5. **Import source** — `csrf`, `user` live in `SecurityMockMvcRequestPostProcessors`; there are sibling classes `SecurityMockMvcResultMatchers` (e.g. `authenticated()`, `unauthenticated()`) and `SecurityMockMvcRequestBuilders` (e.g. `formLogin()` builder). Don't confuse them. ## When to use Reach for post-processors in `@WebMvcTest`/`MockMvc` tests, especially resource-server (`jwt()`) endpoints, negative CSRF tests, and multi-identity scenarios; reach for `@WithMockUser` for simple single-identity method tests.

  • When would you pick jwt() over @WithMockUser?
    When testing an OAuth2 resource-server endpoint. @WithMockUser produces a UsernamePasswordAuthenticationToken, but a resource server expects a JwtAuthenticationToken with a Jwt principal and SCOPE_ authorities; jwt() seeds exactly that without a real issuer or signature verification.
  • Does jwt() verify the token signature or call the JWKS endpoint?
    No. jwt() bypasses decoding entirely and injects a pre-built Jwt/JwtAuthenticationToken. It tests authorization behavior given a valid token, not your JwtDecoder or JWKS wiring. To test decoding you need a real or mocked issuer.

context