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

如何及何时生成新access token?Spring Boot对接API场景下的实现方案咨询

问题解答

1. 方案可行性与最佳实践判定

完全可以仅在access token过期时才生成新的access token,这属于对接OAuth2类第三方API的标准最佳实践。
该方案的优势很明确:

  • 避免了高频请求token端点造成的不必要性能损耗,也降低了被第三方API限流的风险
  • OAuth2协议的原生设计本身就鼓励复用有效access token,仅到期后刷新/重新申请

注意:不要完全卡着官方标注的1小时有效期才换,建议预留30秒到1分钟的缓冲时间,避免因为网络延迟、两端时钟偏差等问题导致请求携带刚过期的token被拒。

2. 具体实现方案

核心思路

用线程安全的存储容器缓存当前有效access token、刷新凭证和过期时间,请求第三方接口前先判断token是否有效,无效则用refresh token换新的token,如果refresh token也过期再走全量重新授权流程即可。

Spring Boot单机部署实现步骤

步骤1:定义线程安全的Token存储实体

public class TokenContext {
    private String accessToken;
    private LocalDateTime accessTokenExpiresAt;
    private String refreshToken;
    private LocalDateTime refreshTokenExpiresAt;

    // 加同步锁保证并发场景下不会重复刷新token
    public synchronized void updateToken(String accessToken, long accessExpiresInSec, 
                                         String refreshToken, long refreshExpiresInSec) {
        this.accessToken = accessToken;
        // 预留60秒缓冲时间
        this.accessTokenExpiresAt = LocalDateTime.now().plusSeconds(accessExpiresInSec - 60);
        this.refreshToken = refreshToken;
        // refresh token也预留1天缓冲
        this.refreshTokenExpiresAt = LocalDateTime.now().plusSeconds(refreshExpiresInSec - 86400);
    }

    public synchronized boolean isAccessTokenValid() {
        return accessToken != null && LocalDateTime.now().isBefore(accessTokenExpiresAt);
    }

    public synchronized boolean isRefreshTokenValid() {
        return refreshToken != null && LocalDateTime.now().isBefore(refreshTokenExpiresAt);
    }
    // 省略对应getter方法
}

步骤2:注册TokenContext为单例Bean

@Configuration
public class ThirdPartyApiConfig {
    @Bean
    public TokenContext tokenContext() {
        return new TokenContext();
    }
}

步骤3:封装Token刷新与获取逻辑

@Service
public class ThirdPartyTokenService {
    @Autowired
    private TokenContext tokenContext;
    @Autowired
    private RestTemplate thirdPartyRestTemplate;

    // 对外暴露的统一获取有效token入口
    public synchronized String getValidAccessToken() {
        if (tokenContext.isAccessTokenValid()) {
            return tokenContext.getAccessToken();
        }
        // access token过期,优先用refresh token刷新
        if (tokenContext.isRefreshTokenValid()) {
            doRefreshToken();
        } else {
            // refresh token也过期,走全量授权流程申请新token
            doApplyFullToken();
        }
        return tokenContext.getAccessToken();
    }

    // 刷新token逻辑,根据第三方API要求调整参数
    private void doRefreshToken() {
        MultiValueMap<String, String> reqParams = new LinkedMultiValueMap<>();
        reqParams.add("grant_type", "refresh_token");
        reqParams.add("refresh_token", tokenContext.getRefreshToken());
        reqParams.add("client_id", "你的客户端ID");
        reqParams.add("client_secret", "你的客户端密钥");
        
        ResponseEntity<Map> resp = thirdPartyRestTemplate.postForEntity(
                "第三方token接口地址", reqParams, Map.class
        );
        Map<String, Object> respData = resp.getBody();
        // 更新存储的token信息
        tokenContext.updateToken(
                respData.get("access_token").toString(),
                Long.parseLong(respData.get("expires_in").toString()),
                respData.get("refresh_token").toString(),
                31 * 86400L // 按题设refresh token有效期31天计算
        );
    }

    // 全量申请新token逻辑,根据你使用的授权模式(客户端模式/授权码模式等)调整实现即可
    private void doApplyFullToken() {
        // 实现逻辑和刷新逻辑类似,仅grant_type和请求参数不同,此处省略
    }
}

步骤4:调用doSomething接口时使用

@Service
public class ThirdPartyApiService {
    @Autowired
    private ThirdPartyTokenService tokenService;
    @Autowired
    private RestTemplate thirdPartyRestTemplate;

    public void callDoSomethingEndpoint() {
        // 获取有效access token
        String accessToken = tokenService.getValidAccessToken();
        // 构造请求
        HttpHeaders headers = new HttpHeaders();
        headers.setBearerAuth(accessToken);
        HttpEntity<Void> reqEntity = new HttpEntity<>(headers);
        // 发起调用
        thirdPartyRestTemplate.postForObject("第三方doSomething接口地址", reqEntity, String.class);
    }
}

可选优化点

  • 如果是分布式部署,把Token存储从内存换成Redis即可,给对应key设置和token有效期一致的过期时间
  • 可以添加接口重试机制:如果携带token请求返回401状态码,直接触发一次token刷新后重试,覆盖极端时钟偏差导致的token校验失败场景
  • 并发量高的场景可以用双重检查锁或者ReentrantLock替换方法级的synchronized,优化性能

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 02:09:02