skip to content

Reactive Security & Testing

Security on the reactive stack, and the test support that makes any of it testable: the WebFlux filter chain, the reactive context holder, @WithMockUser and the MockMvc and WebTestClient security helpers. Interviewers care because untested security configuration is where regressions hide.

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

questions

19

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

open as a page

What is ReactiveSecurityContextHolder and how does it differ from the classic SecurityContextHolder?

level: juniorimportance: must knowfreq 58%

basics

~10 s

In WebFlux, ReactiveSecurityContextHolder stores the logged-in user's SecurityContext inside the Reactor Context (attached to the request's reactive stream) instead of a ThreadLocal, so it survives even when work hops between threads.

open as a page

What does @EnableWebFluxSecurity do, and how do you define a security configuration in a Spring WebFlux application?

level: juniorimportance: must knowfreq 70%

basics

~10 s

@EnableWebFluxSecurity turns on Spring Security for a reactive WebFlux app. You then declare a @Bean of type SecurityWebFilterChain that configures which URLs are protected and how users log in.

open as a page

What does @WithMockUser do in a Spring Security test, and what are its defaults?

level: juniorimportance: must knowfreq 78%

basics

~10 s

@WithMockUser runs a test as if a logged-in user made the request, without hitting a real login. By default the user is named 'user' with password 'password' and the role USER.

open as a page

How do you obtain the authenticated user inside a WebFlux controller or service?

level: middleimportance: must knowfreq 60%

basics

~10 s

Either inject the user as a method parameter (@AuthenticationPrincipal MyUser user, or a Mono<Authentication>/Principal argument), or in a service compose ReactiveSecurityContextHolder.getContext().map(SecurityContext::getAuthentication).

open as a page

How do you declare URL-level access rules in WebFlux using authorizeExchange?

level: middleimportance: must knowfreq 55%

basics

~10 s

Define a SecurityWebFilterChain bean built from ServerHttpSecurity, and call .authorizeExchange(ex -> ex.pathMatchers("/admin/**").hasRole("ADMIN").anyExchange().authenticated()). It's the WebFlux equivalent of MVC's authorizeHttpRequests.

open as a page

What is ReactiveUserDetailsService, how does it fit into WebFlux authentication, and how do you provide one?

level: middleimportance: must knowfreq 60%

basics

~10 s

ReactiveUserDetailsService loads a user by username and returns Mono<UserDetails> instead of blocking. You expose it as a bean; Spring wraps it in an authentication manager that checks the password for username/password logins.

open as a page

When would you use @WithUserDetails instead of @WithMockUser?

level: middleimportance: must knowfreq 62%

basics

~10 s

@WithMockUser fabricates a fake user from annotation attributes. @WithUserDetails loads a real user through your UserDetailsService, so the test uses the same principal type, authorities, and fields your app produces.

open as a page

How do the `jwt()` and `oauth2Login()` request post processors differ, and when would you reach for each in a MockMvc test?

level: middleimportance: should knowfreq 55%

basics

~20 s

jwt() simulates an OAuth2 Resource Server request — it injects a fake JwtAuthenticationToken so you don't need a real token or decoder. oauth2Login() simulates a browser login via an OAuth2/OIDC provider, injecting an OAuth2AuthenticationToken with an OAuth2User. Use jwt() for stateless APIs, oauth2Login() for login-based apps.

open as a page

How does the ServerHttpSecurity DSL differ from the servlet HttpSecurity DSL, and how do you write authorization rules with it?

level: middleimportance: should knowfreq 55%

basics

~10 s

ServerHttpSecurity is the reactive builder. You use authorizeExchange() with pathMatchers() to protect URLs, plus methods like httpBasic() and csrf(), then call build() to get a SecurityWebFilterChain. The servlet version uses authorizeHttpRequests() and requestMatchers().

open as a page

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

level: middleimportance: should knowfreq 40%

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.

open as a page

How do you test a secured reactive (WebFlux) endpoint with `WebTestClient`, and how do the `SecurityMockServerConfigurers` mutators map to the servlet post processors?

level: seniorimportance: should knowfreq 45%

basics

~10 s

For WebFlux, use SecurityMockServerConfigurers with WebTestClient. Apply springSecurity() when binding the client, then per request call .mutateWith(mockUser()), .mutateWith(csrf()), .mutateWith(mockJwt()), or .mutateWith(mockOAuth2Login()). These are the reactive equivalents of the servlet user()/csrf()/jwt()/oauth2Login() post processors.

open as a page

Mechanically, how does the SecurityContext reach downstream operators, and why does Reactor Context propagation direction matter?

level: seniorimportance: should knowfreq 38%

basics

~20 s

Spring Security's AuthenticationWebFilter authenticates the request and calls .contextWrite(...) to place the SecurityContext into the Reactor Context. Because Reactor Context flows bottom-up (from subscriber toward source), that write is visible to all operators above it, i.e. your handler.

open as a page

What is ReactiveAuthenticationManager, how does it drive the authentication flow, and when would you implement a custom one?

level: seniorimportance: should knowfreq 45%

basics

~10 s

ReactiveAuthenticationManager takes an unauthenticated Authentication and returns a Mono of a fully-authenticated one (or an error). It's the reactive strategy that actually verifies credentials, used by the authentication WebFilter.

open as a page

How do you build a custom test annotation with @WithSecurityContext and a WithSecurityContextFactory?

level: seniorimportance: should knowfreq 34%

basics

~10 s

Create your own annotation meta-annotated with @WithSecurityContext(factory = MyFactory.class), then implement WithSecurityContextFactory<YourAnnotation> to build and return a SecurityContext. The listener puts that context into the SecurityContextHolder for the test.

open as a page

How do you test WebFlux endpoints and reactive services that depend on the SecurityContext?

level: principalimportance: should knowfreq 30%

basics

~10 s

Use spring-security-test: annotate tests with @WithMockUser (a ReactorContext test listener injects the context), and for WebTestClient use client.mutateWith(mockUser()) / mockAuthentication() plus mutateWith(csrf()) for writes.

open as a page

Explain the WebFilter-based architecture of the WebFlux security chain and how it differs from the servlet FilterChainProxy model.

level: principalimportance: should knowfreq 30%

basics

~20 s

In WebFlux, security is a chain of Spring WebFilters (not servlet Filters). A WebFilterChainProxy delegates to the matching SecurityWebFilterChain, whose ordered WebFilters do authentication, context loading, and authorization — all returning Mono so nothing blocks the event loop.

open as a page

How do these context annotations behave in reactive WebFlux tests, and what is the idiomatic WebTestClient alternative?

level: principalimportance: should knowfreq 26%

basics

~20 s

In WebFlux, security is read from the reactive context, not a thread-local. @WithMockUser still works in @WebFluxTest slices, but for WebTestClient the idiomatic approach is client.mutateWith(SecurityMockServerConfigurers.mockUser()), which injects the principal into the reactive request context.

open as a page

Mechanically, how does `user()` (and friends) get a `SecurityContext` in front of the filter chain, and how does that interact with `@WithMockUser` and the `springSecurity()` configurer?

level: principalimportance: nice to knowfreq 28%

basics

~20 s

The post processor stores a SecurityContext on the request (via a test SecurityContextRepository) before the filters run. The security filter chain — added by springSecurity() — reads it and populates SecurityContextHolder for that request. @WithMockUser sets the context statically for the whole method; a per-request post processor overrides it.

open as a page