bigywater.tech
Spring · 115

安全测试:@WithMockUser 与 MockMvc

@WithMockUser 到底做了什么?为什么 @WebMvcTest 里的授权测试是绿的、线上却是 403?POST 请求为什么总是 403?测异步代码时用户去哪了?这篇在 Spring Boot 4.0.8、Spring Security 7 上跑 25 个真实的测试用例逐个验证。

假绿
@WebMvcTest 没有 @Import 安全配置时,@WithMockUser(USER)访问 /admin 得到 200
403
@WithMockUser POST 不带 .with(csrf()),或 authorities = "ADMIN" 访问 hasRole("ADMIN") 的接口
null
新开线程里 SecurityContextHolder 的 getAuthentication()

切片不带安全配置

实测 · 本机执行输出
  匿名 GET /public(真实配置里是 permitAll) → HTTP 401 WWW-Authenticate Basic realm="Realm"
  @WithMockUser GET /admin(真实配置里需要 ADMIN) → HTTP 200 admin

@WebMvcTest 不扫描 @Configuration,生效的是 Boot 默认规则,测试结果和线上不一致。

各种测试写法

实测 · 本机执行输出
  匿名 GET /public → HTTP 200 public
  匿名 GET /me → HTTP 401 WWW-Authenticate Basic realm="Realm"
  @WithMockUser GET /me → HTTP 200 UsernamePasswordAuthenticationToken,name=user,authorities=[ROLE_USER]
  @WithMockUser GET /admin → HTTP 403
  @WithMockUser(roles = ADMIN) GET /admin → HTTP 200 admin
  @WithMockUser(authorities = ADMIN) GET /admin → HTTP 403
  @WithMockUser(roles = ADMIN) GET /me → HTTP 200 UsernamePasswordAuthenticationToken,name=user,authorities=[ROLE_ADMIN]
  @WithMockUser POST /items(不带 CSRF) → HTTP 403
  @WithMockUser POST /items(.with(csrf())) → HTTP 200 created
  httpBasic(alice, pw) GET /me → HTTP 200 UsernamePasswordAuthenticationToken,name=alice,authorities=[FactorGrantedAuthority [authority=FACTOR_PASSWORD, issuedAt=2026-10-22T00:45:11.858085Z], ROLE_USER]
  httpBasic(alice, 错误) GET /me → HTTP 401 WWW-Authenticate Basic realm="Realm"
  @WithUserDetails(root) GET /admin → HTTP 200 admin
  @WithUserDetails(root) GET /me → HTTP 200 UsernamePasswordAuthenticationToken,name=root,authorities=[ROLE_ADMIN]
  user("x").roles("ADMIN") GET /admin → HTTP 200 admin
  @WithMockUser(ADMIN) 同时 anonymous() GET /me → HTTP 401 WWW-Authenticate Basic realm="Realm"
  @WithAnonymousUser GET /me → HTTP 401 WWW-Authenticate Basic realm="Realm"

@WithMockUser、httpBasic、@WithUserDetails、user()、csrf() 各自的行为。

方法级安全与线程

实测 · 本机执行输出
  @WithMockUser GET /svc(Service 上有 @PreAuthorize ADMIN) → HTTP 403
  @WithMockUser(ADMIN) GET /svc → HTTP 200 secret
  @WithMockUser 直接调 svc.secret() → 抛 AuthorizationDeniedException
  没有任何用户直接调 svc.secret() → 抛 AuthenticationCredentialsNotFoundException
  @WithMockUser(ADMIN) 直接调 svc.secret() → secret
  测试线程里的用户 = tester;新开的线程里的用户 = null

@PreAuthorize 的异常类型;SecurityContext 只在测试线程里。

standaloneSetup

实测 · 本机执行输出
  匿名 GET /admin → HTTP 200 admin
结果:tests successful 25, tests failed 0, containers failed 0

没有过滤器链,验证不了任何安全规则。

分步演示

安全测试
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK 21.0.12(Homebrew) / Spring Boot 4.0.8(Spring Framework 7.0.9、Tomcat 11.0.24) / Spring Cloud 2025.1.3;用 javac 编译、java 直接运行,依赖由 Maven 从 Maven Central 解析;数字每次运行会有小幅抖动(Spring Security 7.0.7,spring-boot-starter-security-test、spring-boot-starter-webmvc-test、JUnit Jupiter 6.0.3);用 JUnit Platform Console Launcher 直接运行 4 个测试类共 25 个用例,全部通过;应用:/public permitAll、/admin hasRole(ADMIN)、其它要登录,两个用户 alice(USER)与 root(ADMIN),Service 上有 @PreAuthorize
    • 没有验证:@WithMockUser 放在类上的合并规则与 @TestExecutionListeners;@WithSecurityContext 自定义注解;响应式 WebTestClient 的 mutateWith;OAuth2 / JWT 的测试支持(jwt()、oauth2Login());@WebMvcTest 中 CSRF 以外的 Spring Security 过滤器行为;@SpringBootTest 随机端口下用 TestRestTemplate 的安全测试;测试里的 SecurityContext 与 MockMvc 的 TestSecurityContextHolder 传播细节
    京ICP备2026067502号-1京公网安备11010502063487号