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

Spring Boot测试中过期JWT未被拦截的问题咨询

问题:过期JWT令牌请求接口未返回401状态?

在Spring Boot测试中,使用已过期的JWT令牌请求/api/money-transfer接口,预期返回401未授权状态,但实际返回200成功状态。疑惑测试环境下Spring是否不校验JWT过期时间?是需要手动编码校验,还是能让Spring自动完成?


依赖配置

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.4.5</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

<!-- ... -->

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

测试类代码

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;

import java.time.Instant;
import java.time.temporal.ChronoUnit;

import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.jwt;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@AutoConfigureMockMvc
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class TransferControllerTest {

    @Autowired
    MockMvc mockMvc;

    @Test
    void testUnauthorized() throws Exception {
        mockMvc
                .perform(patch("/api/money-transfer")
                        .with(jwt().jwt(jwt -> jwt
                                .claim("userid", 15L)
                                .expiresAt(Instant.now().minus(1, ChronoUnit.DAYS))))
                .andExpect(status().isUnauthorized());
    }
}

控制器代码

package com.example.pixel_money_transfer_api.controller;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/money-transfer")
public class TransferController {

    @PatchMapping
    public ResponseEntity<Void> performTransfer() {
        return ResponseEntity.ok().build(); // gutted every bit of logic
    }
}

错误信息

java.lang.AssertionError: Status expected:<401> but was:<200>
Expected :401
Actual   :200

原因与解决办法

使用SecurityMockMvcRequestPostProcessors.jwt()生成的测试用JWT默认不会触发过期校验——这个处理器只是模拟JWT认证通过的状态,不会自动应用完整的OAuth2资源服务器校验规则。

要让测试环境中Spring自动校验JWT过期时间,可通过以下两种方式解决:

方法1:手动模拟过期认证失败

直接创建标记为未认证的JWT凭证,模拟过期校验失败的场景:

@Test
void testUnauthorized() throws Exception {
    // 构建过期JWT
    Jwt expiredJwt = Jwt.withTokenValue("dummy-token")
            .claim("userid", 15L)
            .expiresAt(Instant.now().minus(1, ChronoUnit.DAYS))
            .build();

    // 创建未认证的JWT令牌对象
    JwtAuthenticationToken authToken = new JwtAuthenticationToken(expiredJwt);
    authToken.setAuthenticated(false);

    mockMvc
            .perform(patch("/api/money-transfer")
                    .with(SecurityMockMvcRequestPostProcessors.authentication(authToken)))
            .andExpect(status().isUnauthorized());
}

方法2:配置完整的OAuth2资源服务器规则

确保Spring Security配置类中正确启用JWT校验,让测试环境自动应用校验逻辑:

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .authorizeHttpRequests(auth -> auth
                        .anyRequest().authenticated())
                .oauth2ResourceServer(oauth2 -> oauth2
                        .jwt(jwt -> jwt
                                .jwtAuthenticationConverter(jwtAuthenticationConverter())));
        return http.build();
    }

    private JwtAuthenticationConverter jwtAuthenticationConverter() {
        JwtAuthenticationConverter converter = new JwtAuthenticationConverter();
        // 可添加自定义权限提取逻辑
        return converter;
    }
}

配置完成后,原测试代码中设置过期时间的JWT会被Spring Security自动校验并返回401。

注意:如果测试类未加载到Security配置类,可通过@Import(SecurityConfig.class)手动导入。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 22:52:39