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

线程休眠30分钟后调用JWT工具类触发IllegalStateException问题

问题解决:休眠后获取JWT令牌触发IllegalStateException

核心原因

  1. 请求上下文已销毁:Spring的RequestContextHolder基于ThreadLocal存储请求属性,Web容器会在请求处理完成后立即清理这些属性。30分钟的休眠远超正常请求生命周期,此时原请求上下文早已被销毁,线程甚至可能被容器回收复用,调用currentRequestAttributes()自然会抛出无线程绑定请求的异常。
  2. 工具类职责错误:你当前的JwtTokenUtils.getToken()方法是从请求头提取已存在的令牌,而非生成新的JWT令牌,完全不符合“生成新令牌调用API”的需求。

解决方案

1. 修正JWT工具类,添加令牌生成能力

重构工具类,明确区分“提取请求中令牌”和“生成新令牌”的职责:

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Base64;

public class JwtTokenUtils {
    private static final Logger log = LoggerFactory.getLogger(JwtTokenUtils.class);

    @Value("${security.jwt.token.secret-key:secret-key}")
    private String secretKey;
    @Value("${security.jwt.token.expire-length:3600000}") // 默认1小时过期,可配置
    private long validityInMilliseconds;

    @PostConstruct
    protected void init() {
        secretKey = Base64.getEncoder().encodeToString(secretKey.getBytes());
    }

    // 生成新的JWT令牌(核心方法)
    public String generateToken(UserDetails userDetails) {
        Map<String, Object> claims = new HashMap<>();
        claims.put("roles", userDetails.getAuthorities());
        return createToken(claims, userDetails.getUsername());
    }

    private String createToken(Map<String, Object> claims, String subject) {
        Date now = new Date();
        Date validity = new Date(now.getTime() + validityInMilliseconds);

        return Jwts.builder()
                .setClaims(claims)
                .setSubject(subject)
                .setIssuedAt(now)
                .setExpiration(validity)
                .signWith(SignatureAlgorithm.HS256, secretKey)
                .compact();
    }

    // 保留原功能:从当前请求提取已有的令牌(添加异常捕获)
    public String extractTokenFromRequest() {
        String authToken = getAuthorizationHeader();
        if (authToken != null && authToken.startsWith("Bearer ")) {
            return authToken.substring(7);
        }
        return null;
    }

    private String getAuthorizationHeader() {
        try {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            return request != null ? request.getHeader("Authorization") : null;
        } catch (IllegalStateException e) {
            log.warn("无线程绑定请求,无法提取Authorization头", e);
            return null;
        }
    }
}

2. 用异步任务处理延迟操作(推荐)

直接在请求线程中休眠30分钟会严重占用Tomcat线程池资源,导致服务性能下降。改用Spring异步任务提前保存用户信息,在独立线程中完成延迟操作:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;

import java.util.concurrent.TimeUnit;

@Service
public class DelayedApiService {
    private static final Logger log = LoggerFactory.getLogger(DelayedApiService.class);

    private final JwtTokenUtils jwtTokenUtils;
    private final RestTemplate restTemplate;
    private final UserDetailsService userDetailsService;

    // 构造注入(替代@Autowired)
    public DelayedApiService(JwtTokenUtils jwtTokenUtils, RestTemplate restTemplate, UserDetailsService userDetailsService) {
        this.jwtTokenUtils = jwtTokenUtils;
        this.restTemplate = restTemplate;
        this.userDetailsService = userDetailsService;
    }

    @Async
    public void executeAfterDelay(String username) {
        try {
            TimeUnit.MINUTES.sleep(30);
            // 根据用户名加载用户信息,生成新令牌
            UserDetails userDetails = userDetailsService.loadUserByUsername(username);
            String newToken = jwtTokenUtils.generateToken(userDetails);
            String accessToken = "Bearer " + newToken;

            // 调用目标API
            HttpHeaders headers = new HttpHeaders();
            headers.set("Authorization", accessToken);
            HttpEntity<String> entity = new HttpEntity<>(headers);
            ResponseEntity<String> response = restTemplate.exchange("https://your-target-api-url", HttpMethod.GET, entity, String.class);
            // 处理API响应
            log.debug("API调用结果: {}", response.getBody());
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("延迟任务被中断", e);
        }
    }

    // 在请求处理方法中调用异步任务
    public void handleOriginalRequest() {
        // 从当前Security上下文获取用户名
        String username = ((UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername();
        // 触发异步延迟任务,立即返回响应给客户端
        executeAfterDelay(username);
    }
}

3. 若必须在原线程处理(不推荐)

如果业务限制必须在原线程执行,需提前保存用户信息,避免依赖已销毁的请求上下文:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;

import java.time.Instant;
import java.util.concurrent.TimeUnit;

public class OriginalThreadHandler {
    private static final Logger log = LoggerFactory.getLogger(OriginalThreadHandler.class);
    private final JwtTokenUtils jwtTokenUtils;
    private final UserDetailsService userDetailsService;

    public OriginalThreadHandler(JwtTokenUtils jwtTokenUtils, UserDetailsService userDetailsService) {
        this.jwtTokenUtils = jwtTokenUtils;
        this.userDetailsService = userDetailsService;
    }

    public void process() throws InterruptedException {
        log.debug("Reached here ======================== {}", Instant.now());

        // 休眠前,从当前Security上下文获取并保存用户信息
        UserDetails userDetails = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        String username = userDetails.getUsername();

        TimeUnit.MINUTES.sleep(30);
        log.error("again starting {}", Instant.now());

        // 使用保存的用户名生成新令牌
        UserDetails newUserDetails = userDetailsService.loadUserByUsername(username);
        String newToken = jwtTokenUtils.generateToken(newUserDetails);
        String accessToken = "Bearer " + newToken;

        // 调用API...
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 02:12:08