
本文详解如何在 Spring Boot 集成测试中正确模拟认证上下文,解决因自定义 OncePerRequestFilter 未被 MockMvc 自动触发而导致的 401 Unauthorized 问题。
本文详解如何在 spring boot 集成测试中正确模拟认证上下文,解决因自定义 `onceperrequestfilter` 未被 mockmvc 自动触发而导致的 401 unauthorized 问题。
在 Spring Security 集成测试中,MockMvc 默认不会执行实际的过滤器链(包括你注册的 AuthGatewayFilter),而是通过 SecurityMockMvcRequestPostProcessors 或注解方式模拟安全上下文。因此,即使你的 AuthGatewayFilter 在运行时能成功设置 Authentication,在测试中它根本不会被调用——SecurityContextHolder.getContext().getAuthentication() 仍为 null,最终触发默认的 401 响应。
✅ 正确做法:使用 @WithMockUser 模拟认证
最简洁、推荐的方式是直接使用 Spring Security Test 提供的 @WithMockUser 注解,它会在测试执行前自动将指定用户注入 SecurityContext,绕过过滤器链依赖:
@WithMockUser(username = "test@example.com", authorities = {"ROLE_USER"})
@Test
void securityCheckWithOkta() throws Exception {
mockMvc.perform(get("/security-check")
.header("Authorization", "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...")
.header("companyId", "1438"))
.andExpect(status().isOk())
.andReturn();
}⚠️ 注意:@WithMockUser 中的 username 必须与你在 AuthGatewayFilter 中硬编码的 UsernamePasswordAuthenticationToken 主体一致(如 <a class="__cf_email__" data-cfemail="6717150e09040e17060b271417150e0900050808134904080a" href="/cdn-cgi/l/email-protection">[email protected]</a>),否则 @PreAuthorize 或 authenticated() 校验会失败。
? 进阶方案:手动配置 MockMvc 安全上下文(适用于复杂场景)
若需真实触发自定义过滤器逻辑(例如验证 JWT 解析、租户头校验等),可禁用默认安全配置,并显式注册你的过滤器:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
class Auth0IntegrationControllerTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private AuthGatewayFilter authGatewayFilter; // 确保 Bean 可注入
@Test
void securityCheckWithRealFilter() throws Exception {
mockMvc = MockMvcBuilders.webAppContextSetup(context)
.addFilter(authGatewayFilter, "/**") // 显式添加过滤器
.apply(springSecurity()) // 启用 Security 支持
.build();
mockMvc.perform(get("/security-check")
.header("Authorization", "Bearer ...")
.header("companyId", "1438"))
.andExpect(status().isOk());
}
}但此方式需确保 authGatewayFilter 在测试上下文中已正确初始化(如 @MockBean 或 @Import 配置类),且其内部逻辑不依赖生产环境 Bean(如 JwtDecoder)。
? 关键注意事项
- ❌ 不要依赖 @WithMockUser + 实际 JWT 头:@WithMockUser 会覆盖请求头中的 Authorization,JWT 不会被解析。
- ✅ 若测试目标是端到端 JWT 验证流程,应使用 @WebMvcTest + @Import(WebSecurityConfig.class) 并配合 SecurityMockMvcRequestPostProcessors.jwt()(Spring Security 5.6+):
mockMvc.perform(get("/security-check") .with(jwt().authorities(new SimpleGrantedAuthority("SCOPE_read"))) .header("companyId", "1438")) .andExpect(status().isOk()); - ? 自定义过滤器中请避免硬编码用户名;建议改用 JwtAuthenticationConverter 或提取为可配置策略,便于测试与维护。
总之,401 并非配置错误,而是测试与运行时环境差异所致。选择 @WithMockUser 是快速验证授权逻辑的黄金标准;而需验证过滤器行为时,则应转向 MockMvcBuilders 手动构建并启用完整过滤链。

















