What are SecurityMockMvcRequestPostProcessors (user(), jwt(), csrf()) and how do they differ from @WithMockUser?
answer
- static import, .with(user()/jwt()/csrf())
- per-request & imperative vs annotation per-method
- jwt() = mock decoded JWT, no signature check
- csrf().useInvalidToken() for negative path
- SCOPE_ authorities from jwt scopes
basics
~20 sThey 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 sSecurityMockMvcRequestPostProcessors 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 linesimport 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
Know .with(csrf()) is needed for POST and .with(user(...)) authenticates one request.
Explain per-request vs per-method, jwt() for resource servers, and the roles/authorities/scope prefixing.
Discuss testing negative CSRF (useInvalidToken), multiple identities per test, and that jwt() skips signature/issuer validation.
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.