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

Spring Boot集成测试MockMvc访问JWT安全端点返回401问题

问题根因

你当前的服务是基于JWT的无状态认证架构,所有受保护请求必须先经过自定义的JwtAuthenticationTokenFilter校验请求头中的合法JWT令牌,才会将认证信息写入Spring Security上下文。你通过.with(authentication(...))或者@MockUser提前构造的测试身份,会被前置的JWT过滤器直接覆盖/清空——JWT过滤器的默认逻辑是:只要请求未携带合法JWT,就不会读取Security上下文中预置的认证对象,直接判定为未认证,返回401。
另外你还有一处权限配置的隐性坑:配置中使用的.hasRole("API_ADMIN")方法,Spring Security会自动为权限字符串拼接ROLE_前缀做匹配,你手动构造的权限标识是API_ADMIN,即使认证通过也会因权限标识不匹配返回403。

解决方案

方案1:测试请求携带真实生成的合法JWT(最贴合生产逻辑,推荐)

该方案不需要修改生产环境安全配置,完全模拟真实请求链路:

  • 在测试类中注入项目中使用的JWT生成工具,测试执行前生成绑定API_ADMIN权限的有效JWT
  • 发起请求时将JWT放入Authorization请求头,格式遵循Bearer {token}规范
    示例代码片段:
@Autowired
private JwtTokenUtil jwtTokenUtil; // 替换为你项目中实际的JWT工具类

@Test
void getAllTransactionsSuccessTest() throws Exception {
    String adminToken = jwtTokenUtil.generateToken("admin_user", Collections.singletonList("API_ADMIN"));
    this.mockMvc.perform(get("/transactions/account?account_id=1")
            .header("Authorization", "Bearer " + adminToken))
            .andDo(print())
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.totalElements").value(50))
            .andExpect(jsonPath("$.transactions").exists());
}

注意:如果你的JwtAuthenticationProvider包含用户存在性校验,需要通过@Sql注解提前插入测试用户数据,或者Mock用户查询逻辑,避免因用户不存在导致认证失败。

方案2:测试环境调整安全配置,跳过JWT强制校验

如果不需要在集成测试中覆盖JWT校验逻辑,可以单独为测试环境编写优先级更高的安全配置,跳过自定义JWT过滤器,直接使用MockMvc传入的认证信息:

  1. 在test目录下创建测试专用安全配置,覆盖生产配置:
@Configuration
@Order(1)
public class TestSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .cors()
                .and()
                .authorizeRequests()
                .antMatchers("/transactions/all").hasRole("API_ADMIN")
                .antMatchers("/auth/token", "/v2/api-docs", "/swagger-resources/**", "/swagger-ui.html", "/webjars/**").permitAll()
                .anyRequest().authenticated()
                .and()
                .exceptionHandling().authenticationEntryPoint(unauthorizedHandler)
                .and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        // 测试环境不注册JWT认证过滤器,避免覆盖Mock传入的认证信息
    }
}
  1. 修正测试代码中构造认证对象的权限前缀,匹配hasRole()的匹配规则:
@Test
void getAllTransactionsSuccessTest() throws Exception {
    this.mockMvc.perform(get("/transactions/account?account_id=1")
            .with(authentication(new UsernamePasswordAuthenticationToken(
                    "admin_user",
                    null,
                    Collections.singletonList(new SimpleGrantedAuthority("ROLE_API_ADMIN"))
            ))))
            .andDo(print())
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.totalElements").value(50))
            .andExpect(jsonPath("$.transactions").exists());
}
额外排错点
  • 你当前同时使用了@AutoConfigureMockMvc自动注入MockMvc,又在@BeforeEach中手动通过MockMvcBuilders重复构建MockMvc实例,重复构建可能导致过滤器链加载异常。使用@AutoConfigureMockMvc时直接注入自动生成的MockMvc即可,不需要手动重复构建。
  • 检查JwtAuthenticationTokenFilter的实现逻辑:如果过滤器在请求未携带JWT时主动执行SecurityContextHolder.getContext().setAuthentication(null)清空上下文,所有提前预置的Mock认证信息都会被清除,这也是@MockUser注解不生效的核心原因。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 08:25:30