You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring WebMvcTest使用@WithMockUser配置角色仍返回HTTP403

问题根因

返回403的核心原因是Spring Security默认开启的CSRF防护拦截了请求,和用户角色配置无关:

  • 当@WebMvcTest未加载自定义SecurityFilterChain Bean时,会自动启用Spring Security默认安全规则:仅放行GET/HEAD/TRACE/OPTIONS方法的CSRF校验,其余HTTP方法必须携带合法CSRF令牌才能通过过滤器链
  • DELETE请求未携带CSRF令牌时,会在到达Controller层之前被CSRF过滤器直接拦截返回403,对应日志中Handler: Type = null的现象,@WithMockUser配置的角色信息不会参与这一阶段的校验。
    GET方法测试可正常通过、手动调用接口正常的现象也可佐证这一点:生产环境自定义的SecurityFilterChain通常会针对无状态REST接口关闭CSRF防护,手动调用不会触发CSRF校验,但@WebMvcTest未加载该自定义配置,使用的是默认开启CSRF的安全规则。
轻量化解决方案

方案1:测试请求附加CSRF令牌(零额外配置,优先选用)

无需新增任何配置类、无需修改测试上下文加载规则,仅需在构造MockMvc请求时通过Spring Security测试工具内置的csrf()请求处理器附加合法令牌即可,改动量最小:

// 新增静态导入
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;

@Test
@WithMockUser(roles={"USER","ADMIN"})
void shouldReturn204ForDeleteProduct() throws Exception {
  var productId = 1;
  
  mockMvc.perform(delete(SINGLE_RESOURCE_ENDPOINT_URL, productId)
          .with(csrf()) // 附加合法CSRF令牌,通过CSRF过滤器校验
      )
      .andExpect(status().isNoContent());
}

所有POST/PUT/DELETE/PATCH等非GET方法的测试用例,都可以通过追加.with(csrf())的方式快速通过CSRF校验,不会引入额外的上下文加载开销。

方案2:测试类内嵌最小安全配置(无额外类文件依赖)

如果对应REST接口本身是无状态服务,生产环境也会关闭CSRF防护,可以直接在测试类内部内嵌一个最小化的安全配置,不需要引入项目中完整的Spring Security配置:

// 新增导入
import org.springframework.context.annotation.Import;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.context.annotation.Configuration;

@ExtendWith(SpringExtension.class)
@ContextConfiguration
@WebMvcTest({ProductInformationController.class, CommonControllerAdvice.class})
@Import(ProductInformationControllerTest.TestSecurityConfig.class) // 导入内嵌的测试安全配置
class ProductInformationControllerTest {

  // 内嵌测试专用安全配置,无需新建独立类文件
  @Configuration
  @EnableWebSecurity
  static class TestSecurityConfig {
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
      http
        .csrf(csrf -> csrf.disable()) // 测试环境关闭CSRF校验
        .authorizeHttpRequests(auth -> auth.anyRequest().authenticated()); // 保持基础认证规则
      return http.build();
    }
  }

  private static final String SINGLE_RESOURCE_ENDPOINT_URL = "/product/{id}";

  @Autowired
  MockMvc mockMvc;

  @MockBean
  ProductInformationService service;

  @Test
  @WithMockUser(roles={"USER","ADMIN"})
  void shouldReturn204ForDeleteProduct() throws Exception {
    var productId = 1;
    
    mockMvc.perform(delete(SINGLE_RESOURCE_ENDPOINT_URL, productId))
        .andExpect(status().isNoContent());
  }

  // 其余测试方法保持不变
}

该配置仅加载测试必需的安全规则,不会引入项目中完整安全配置的多余依赖,上下文加载成本极低。

内容的提问来源于stack exchange,提问作者void

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 03:06:19