在Spring Boot应用对接第三方服务或者内部微服务时,经常会遇到短生命周期令牌的使用场景,这类令牌通常有效期只有几分钟到几小时,过期后需要重新获取才能正常发起请求,因此实现定时刷新令牌的逻辑是保障业务稳定运行的关键。

短生命周期令牌的使用痛点
短生命周期令牌的设计初衷是提升系统安全性,减少令牌泄露后的风险,但也给应用开发带来了额外的工作量。如果令牌过期后没有及时刷新,应用发起的请求会直接返回鉴权失败的错误,影响业务流程。常见的痛点包括:
- 手动刷新令牌效率低,容易遗漏
- 多实例部署时令牌刷新逻辑冲突,导致重复获取
- 令牌刷新失败没有重试机制,影响后续请求
基于Spring Scheduled的定时刷新策略
Spring Boot自带了定时任务的支持,通过@Scheduled注解可以快速实现定时刷新令牌的逻辑,适合单实例部署的场景。
基础实现步骤
首先需要开启Spring Boot的定时任务支持,在启动类上添加@EnableScheduling注解:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableScheduling
public class TokenRefreshApplication {
public static void main(String[] args) {
SpringApplication.run(TokenRefreshApplication.class, args);
}
}
接下来实现令牌管理类,封装令牌的获取和刷新逻辑:
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
@Component
public class TokenManager {
// 存储当前有效的令牌
private String currentToken;
// 令牌过期时间
private LocalDateTime expireTime;
/**
* 初始化令牌,应用启动时执行一次
*/
public TokenManager() {
refreshToken();
}
/**
* 获取当前有效令牌,如果令牌即将过期则主动刷新
*/
public String getToken() {
// 令牌剩余有效期小于1分钟时主动刷新
if (expireTime != null && expireTime.minusMinutes(1).isBefore(LocalDateTime.now())) {
refreshToken();
}
return currentToken;
}
/**
* 定时刷新令牌,每30分钟执行一次,令牌有效期假设为1小时
*/
@Scheduled(cron = "0 0/30 * * * ?")
public void refreshToken() {
try {
// 模拟调用第三方接口获取新令牌的逻辑
// 实际场景中替换为真实的令牌获取请求
String newToken = "new_token_" + System.currentTimeMillis();
currentToken = newToken;
// 假设新令牌有效期为1小时
expireTime = LocalDateTime.now().plusHours(1);
System.out.println("令牌刷新成功,新令牌:" + newToken + ",过期时间:" + expireTime);
} catch (Exception e) {
System.out.println("令牌刷新失败,错误信息:" + e.getMessage());
// 可以添加重试逻辑或者告警通知
}
}
}
在业务调用处注入TokenManager获取令牌即可:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@Autowired
private TokenManager tokenManager;
@GetMapping("/test")
public String test() {
String token = tokenManager.getToken();
// 使用token发起第三方请求
return "当前使用的令牌:" + token;
}
}
多实例部署的令牌刷新优化策略
如果Spring Boot应用是多实例部署,直接使用上面的定时任务会导致每个实例都执行令牌刷新逻辑,可能出现重复获取令牌的问题,此时可以结合分布式锁来保证同一时间只有一个实例执行刷新操作。
以Redis分布式锁为例,改造refreshToken方法:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;
@Component
public class DistributedTokenManager {
private String currentToken;
private LocalDateTime expireTime;
@Autowired
private RedisTemplate<String, String> redisTemplate;
// 分布式锁的key
private static final String LOCK_KEY = "token_refresh_lock";
// 锁的过期时间,避免死锁
private static final long LOCK_EXPIRE = 10L;
public DistributedTokenManager() {
refreshToken();
}
public String getToken() {
if (expireTime != null && expireTime.minusMinutes(1).isBefore(LocalDateTime.now())) {
refreshToken();
}
return currentToken;
}
@Scheduled(cron = "0 0/30 * * * ?")
public void refreshToken() {
// 尝试获取分布式锁
Boolean lock = redisTemplate.opsForValue().setIfAbsent(LOCK_KEY, "1", LOCK_EXPIRE, TimeUnit.SECONDS);
if (Boolean.TRUE.equals(lock)) {
try {
// 获取锁成功,执行令牌刷新逻辑
String newToken = "new_token_" + System.currentTimeMillis();
currentToken = newToken;
expireTime = LocalDateTime.now().plusHours(1);
System.out.println("分布式环境令牌刷新成功,新令牌:" + newToken);
} catch (Exception e) {
System.out.println("令牌刷新失败:" + e.getMessage());
} finally {
// 释放锁
redisTemplate.delete(LOCK_KEY);
}
} else {
System.out.println("当前实例未获取到锁,跳过令牌刷新");
}
}
}
策略选择的注意事项
选择定时刷新策略时需要结合实际的业务场景:
- 单实例部署优先选择基础的Spring Scheduled方案,实现简单维护成本低
- 多实例部署必须引入分布式锁,避免令牌重复获取的问题
- 令牌刷新失败需要添加重试机制,比如失败后间隔1分钟再次尝试刷新,连续失败多次后触发告警
- 可以在令牌管理类中添加令牌有效性的校验逻辑,避免返回过期令牌给业务调用方
实际开发中需要根据令牌的真实有效期调整定时刷新的时间间隔,通常建议刷新间隔设置为令牌有效期的二分之一到三分之二,预留足够的缓冲时间处理刷新异常的情况。
Spring_Boot定时任务令牌刷新短生命周期令牌修改时间:2026-07-16 12:51:32