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

Spring Boot应用调用外部API时如何复用未过期的access token?

Spring Boot 下AccessToken复用最优方案

1. 框架原生支持方案:Spring Security OAuth2 Client

Spring官方原生提供了OAuth2客户端能力,内置token缓存、自动刷新、过期判断逻辑,完全不需要手动实现缓存相关代码:

  • 第一步引入依赖:
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-oauth2-client</artifactId>
</dependency>
  • 第二步在配置文件中添加3个API对应的客户端配置:
spring:
  security:
    oauth2:
      client:
        registration:
          # 第一个API的客户端配置
          api1:
            client-id: 替换为api1的客户端ID
            client-secret: 替换为api1的客户端密钥
            authorization-grant-type: client_credentials
            scope: 替换为api1要求的权限范围
          # 第二个API的客户端配置
          api2:
            client-id: 替换为api2的客户端ID
            client-secret: 替换为api2的客户端密钥
            authorization-grant-type: client_credentials
            scope: 替换为api2要求的权限范围
          # 第三个API的客户端配置
          api3:
            client-id: 替换为api3的客户端ID
            client-secret: 替换为api3的客户端密钥
            authorization-grant-type: client_credentials
            scope: 替换为api3要求的权限范围
        provider:
          api1:
            token-uri: 替换为api1获取token的接口地址
          api2:
            token-uri: 替换为api2获取token的接口地址
          api3:
            token-uri: 替换为api3获取token的接口地址
  • 第三步直接注入官方工具类获取token即可,框架会自动判断过期状态,未过期直接返回缓存值,过期自动触发刷新,同时内置并发控制避免多线程同时刷新token:
@Autowired
private OAuth2AuthorizedClientManager authorizedClientManager;

public String getApi1Token() {
    OAuth2AuthorizeRequest request = OAuth2AuthorizeRequest.withClientRegistrationId("api1")
            .principal("anonymous")
            .build();
    OAuth2AuthorizedClient client = authorizedClientManager.authorize(request);
    return client.getAccessToken().getTokenValue();
}

2. 轻量优化方案(不引入Spring Security)

你当前基于volatile的实现存在高并发场景下多线程同时判断token过期、重复发起token请求的问题,可通过以下方式优化:

  • 加双重检查锁避免并发刷新,同时增加提前过期逻辑避免客户端与授权服务器时间差导致的token生效问题:
private volatile String accessToken;
private volatile Date expiryTime;
private final Object lock = new Object();

public String getValidToken() {
    // 第一层判断:未过期直接返回
    if (!hasExpired()) {
        return accessToken;
    }
    synchronized (lock) {
        // 第二层判断:拿到锁后再校验一次,避免前面已经有线程刷新过token
        if (!hasExpired()) {
            return accessToken;
        }
        // 调用接口获取新token
        TokenResponse newToken = requestNewToken();
        this.accessToken = newToken.getToken();
        // 提前1分钟过期,容错时间差、网络延迟问题
        this.expiryTime = new Date(System.currentTimeMillis() + (newToken.getExpiresIn() - 60) * 1000);
        return accessToken;
    }
}

private boolean hasExpired() {
    if (ObjectUtils.isEmpty(this.expiryTime))
        return true;
    return new Date().after(this.expiryTime);
}
  • 也可以配合Spring缓存注解+本地缓存实现,代码更简洁:
// 启动类添加@EnableCaching注解开启缓存
@Cacheable(value = "api1Token", expireAfterWrite = 29, timeUnit = TimeUnit.MINUTES)
public String getApi1Token() {
    // 直接实现获取token的逻辑即可,缓存自动29分钟过期
    return requestNewToken().getToken();
}

内容的提问来源于stack exchange,提问作者Anoop M Nair

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 14:57:03