skip to content

In a Spring MockMvc test, how do you make a request run as an authenticated user, and why does a POST often need `.with(csrf())`?

level: juniorimportance: must knowfreq 70%

answer

  1. SecurityMockMvcRequestPostProcessors static import
  2. .with(user(...)) attaches a per-request SecurityContext
  3. roles() adds ROLE_ prefix, authorities() verbatim
  4. csrf() needed for POST/PUT/DELETE when CSRF enabled
  5. csrf().asHeader() / useInvalidToken()

basics

~10 s

Use SecurityMockMvcRequestPostProcessors: mockMvc.perform(get("/").with(user("alice").roles("ADMIN"))) runs the request as that user. State-changing requests (POST/PUT/DELETE) need .with(csrf()) because CSRF protection rejects them without a valid token.

solid answer

~40 s

Spring Security ships `SecurityMockMvcRequestPostProcessors` (static-import it). `user("alice").roles("ADMIN")` builds a `SecurityContext` holding a `UsernamePasswordAuthenticationToken` and attaches it to the request, so the filter chain sees an authenticated principal for that single request — you attach it with `MockHttpServletRequestBuilder.with(...)`. `csrf()` injects a valid CSRF token where `CsrfFilter` expects it. When CSRF protection is enabled (the default for stateful/form login), any mutating method (POST, PUT, PATCH, DELETE) is rejected with 403 unless a valid token is present, so tests add `.with(csrf())`. You can chain them: `.with(user("alice")).with(csrf())`. For negative tests, `csrf().useInvalidToken()` forces a failure. This all requires the security filter chain to be wired into MockMvc — automatic under `@SpringBootTest` + `@AutoConfigureMockMvc`.

code

java · 26 lines
java
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@SpringBootTest
@AutoConfigureMockMvc
class AdminControllerTest {

    @Autowired MockMvc mockMvc;

    @Test
    void adminCanCreate() throws Exception {
        mockMvc.perform(post("/api/widgets")
                        .with(user("alice").roles("ADMIN"))
                        .with(csrf())
                        .contentType(MediaType.APPLICATION_JSON)
                        .content("{\"name\":\"w1\"}"))
               .andExpect(status().isCreated());
    }

    @Test
    void missingCsrfIsForbidden() throws Exception {
        mockMvc.perform(post("/api/widgets").with(user("alice").roles("ADMIN")))
               .andExpect(status().isForbidden());
    }
}

go deeper

for a junior

Know the two everyday post processors: user() for auth, csrf() for mutating requests, and that you attach them with .with().

for a middle

Explain the 403-vs-401 distinction and when csrf() is unnecessary (stateless bearer-token APIs).

for a senior

Discuss the wiring requirement (springSecurity() configurer) and roles/authorities exclusivity.

for a principal

Frame per-request post processors vs annotation-based static context and how CSRF policy in the SecurityFilterChain drives test needs.

## What these are `SecurityMockMvcRequestPostProcessors` is a utility class in `spring-security-test` (package `org.springframework.security.test.web.servlet.request`). It provides static factory methods that return `RequestPostProcessor` objects — small hooks that mutate a `MockHttpServletRequest` just before MockMvc dispatches it. You attach one with the `with(...)` method on a request builder: ```java import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; mockMvc.perform(get("/admin").with(user("alice").roles("ADMIN"))) .andExpect(status().isOk()); ``` ## `user(...)` `user("alice")` creates a lightweight authenticated context for just this request. Under the hood it builds a `UsernamePasswordAuthenticationToken` and puts it into a `SecurityContext` that the security filter chain reads. Modifiers: - `.roles("ADMIN", "USER")` — each becomes an authority prefixed with `ROLE_` (so `ROLE_ADMIN`). - `.authorities("SCOPE_read")` — sets authorities verbatim, no prefix. (`roles` and `authorities` are mutually exclusive — the last one you call wins.) - `.password(...)`, `.name(...)` — rarely needed for authorization tests. There is also an overload `user(UserDetails)` when you already have a domain user. ## `csrf()` Spring Security's `CsrfFilter` protects state-changing HTTP methods (anything that is not GET/HEAD/OPTIONS/TRACE) by requiring a CSRF token. In a real browser the token arrives from a prior page; in a MockMvc test there is no prior page, so the request has no token and `CsrfFilter` returns **403 Forbidden** via `AccessDeniedHandler`. `csrf()` supplies a token that matches what the configured `CsrfTokenRepository` expects, so the request passes. Variants: - `csrf()` — places the token as a request parameter (`_csrf`). - `csrf().asHeader()` — sends it as the `X-CSRF-TOKEN` header instead (useful for JSON/AJAX-style endpoints). - `csrf().useInvalidToken()` — deliberately sends a bad token so you can assert the 403 path. ## When csrf() is NOT needed If CSRF protection is disabled — common for **stateless** APIs secured by a bearer token (OAuth2 Resource Server), where you'd typically configure `http.csrf(csrf -> csrf.disable())` — the filter is absent and adding `.with(csrf())` is harmless but unnecessary. GET requests never need it. ## Wiring requirement These post processors only work if the Spring Security filter chain is part of the MockMvc instance. With `@SpringBootTest` + `@AutoConfigureMockMvc` it is auto-applied. If you build MockMvc manually (`MockMvcBuilders.webAppContextSetup(context)`), you must add `.apply(SecurityMockMvcConfigurers.springSecurity())`, otherwise the filters never run and `user()`/`csrf()` silently do nothing. ## Chaining Each `with(...)` adds one post processor; call it multiple times to combine: `.with(user("alice").roles("ADMIN")).with(csrf())`.

  • What is the difference between `.roles("ADMIN")` and `.authorities("ROLE_ADMIN")` on `user()`?
    `roles("ADMIN")` automatically prepends `ROLE_`, giving authority `ROLE_ADMIN`. `authorities("ROLE_ADMIN")` sets it verbatim. They are equivalent here, but `roles` and `authorities` are mutually exclusive — calling both means the last one wins.
  • Your POST test returns 403 even though you added `.with(user(...))`. What's the likely cause?
    CSRF protection is enabled and the request has no token — add `.with(csrf())`. (403, not 401, is the tell: the user IS authenticated but the CSRF check fails.)

saying these in an interview costs you the question

  • Thinking `.with(user(...))` alone lets a POST through when CSRF is enabled
  • Believing csrf() logs a user in (it only supplies a token; user() handles auth)
  • Assuming roles("ROLE_ADMIN") works — it double-prefixes to ROLE_ROLE_ADMIN

context