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

如何在Spring Boot与Mockito测试中模拟JWT Token完成鉴权接口测试

问题根因

你当前的测试失败主要由以下3个问题导致:

  1. Mock对象未配置打桩规则:你已经通过@MockBean注入了JwtUtil、MyUserDetailsService,但没有为这些Mock对象的方法预设返回值,过滤器调用jwtUtil.extractUsername()、userDetailsService.loadUserByUsername()时会返回null,导致鉴权流程中断,请求不会走到控制器层,自然返回空响应体。
  2. JwtRequestFilter存在逻辑漏洞:当Authorization头合法但解析出的用户名为空时,过滤器既没有返回错误响应,也没有调用后续过滤器链,请求被直接挂起,最终返回空内容。
  3. 测试打桩顺序错误:你将categoryCommandService的打桩逻辑放在了第一次mockMvc.perform()之后,虽然第二次请求用到了打桩规则,但鉴权失败的情况下请求根本到不了服务层。

修复方案

第一步:修复JWT过滤器逻辑

修改JwtRequestFilter的doFilterInternal方法,补全异常分支的响应逻辑:

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
        throws ServletException, IOException {
    String path = request.getRequestURI();
    if (path.equals(SIGN_UP_URL)) {
        chain.doFilter(request, response);
        return;
    }
    final String authorizationHeader = request.getHeader("Authorization");
    String username = null;
    String jwt = null;
    if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
        jwt = authorizationHeader.substring(7);
        username = jwtUtil.extractUsername(jwt);
    } else {
        response.setStatus(HttpStatus.FORBIDDEN.value());
        return; // 非法请求直接返回,不继续执行
    }
    if (username != null) {
        UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
        if (jwtUtil.validateToken(jwt, userDetails)) {
            UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
                    userDetails, null, userDetails.getAuthorities());
            usernamePasswordAuthenticationToken
                    .setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
            SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
        }
        chain.doFilter(request, response);
    } else {
        // 用户名解析失败直接返回403
        response.setStatus(HttpStatus.FORBIDDEN.value());
        return;
    }
}

第二步:调整测试逻辑,补全Mock打桩规则

修改测试方法,先配置所有Mock对象的返回规则,再发起请求:

@Test
void testCreateCategory() throws Exception {
    // 1. 构造测试数据
    CategoryCreateDto categoryCreateDto = new CategoryCreateDto("category");
    CategoryCreateDto categoryCreateResponseDto = new CategoryCreateDto(UUID.fromString("2da4002a-31c5-4cc7-9b92-cbf0db998c41"), "category");
    String jsonCreate = asJsonString(categoryCreateDto);
    String jsonResponse = asJsonString(categoryCreateResponseDto);

    // 2. 补全JWT相关Mock对象的打桩规则
    UserDetails mockUser = new User("foo@email.com", "password", Collections.emptyList());
    when(jwtUtil.extractUsername(anyString())).thenReturn("foo@email.com");
    when(myUserDetailsService.loadUserByUsername("foo@email.com")).thenReturn(mockUser);
    when(jwtUtil.validateToken(anyString(), eq(mockUser))).thenReturn(true);

    // 3. 配置业务服务的打桩规则
    when(categoryCommandService.createCategory(categoryCreateDto)).thenReturn(categoryCreateResponseDto);

    // 4. 构造请求并执行测试
    RequestBuilder request = MockMvcRequestBuilders
            .post("/api/adverts/category")
            .content(jsonCreate)
            .header("Authorization", "Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJmb29AZW1haWwuY29tIiwiZXhwIjoxNjM4ODU1MzA1LCJpYXQiOjE2Mzg4MTkzMDV9.q4FWV7yVDAs_DREiF524VZ-udnqwV81GEOgdCj6QQAs")
            .contentType(MediaType.APPLICATION_JSON_VALUE)
            .accept(MediaType.APPLICATION_JSON);
    
    MvcResult mvcResult = mockMvc.perform(request)
            .andExpect(status().is2xxSuccessful())
            .andExpect(content().json(jsonResponse, true))
            .andExpect(jsonPath("$.id").value("2da4002a-31c5-4cc7-9b92-cbf0db998c41"))
            .andExpect(jsonPath("$.title").value("category"))
            .andReturn();

    logger.info(mvcResult.getResponse().getContentAsString());
}

可选优化方案

如果你不想模拟JWT工具类,可以直接使用真实的JwtUtil生成有效token,更贴近实际运行场景:

  1. 移除@MockBean private JwtUtil jwtUtil;,直接注入真实的JwtUtil实例
  2. 测试中直接生成token:
@Autowired
private JwtUtil jwtUtil;

// 测试方法内生成有效token
UserDetails mockUser = new User("foo@email.com", "password", Collections.emptyList());
String validToken = jwtUtil.generateToken(mockUser);
// 请求头传 "Bearer " + validToken 即可

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.23 22:06:02