API Token有效期管理:从JWT到动态刷新的最佳实践

1次阅读
没有评论

共计 2556 个字符,预计需要花费 7 分钟才能阅读完成。

image.webp

背景痛点

在微服务架构中,API Token 的有效期管理是系统安全和用户体验之间的重要平衡点。固定有效期的 Token 虽然简单,但存在几个明显的痛点:

API Token 有效期管理:从 JWT 到动态刷新的最佳实践

  • 安全风险 :如果 Token 被泄露,攻击者可以在其有效期内肆意使用,导致窗口期攻击。
  • 用户体验 :Token 过期后用户需要频繁重新登录,尤其是在移动端应用中,这会显著降低用户体验。
  • 错误率 :据统计,Token 失效引发的接口错误率在某些业务场景下可以占到总错误率的 30% 以上。

技术方案

短期 JWT + Refresh Token

相比于传统的 Session 方案,短期 JWT(如 15 分钟有效期)配合长时效 Refresh Token(如 7 天)的方案有以下优势:

  • 安全性 :短期 JWT 降低了泄露后的风险窗口。
  • 无状态 :JWT 本身是无状态的,减少了服务端的存储压力。
  • 自动续期 :Refresh Token 可以在用户无感知的情况下自动续期 Token,避免频繁登录。

OAuth2 Refresh Token 工作流程

以下是 OAuth2 中 Refresh Token 的工作流程:

  1. 用户首次登录,获取 Access Token 和 Refresh Token。
  2. Access Token 过期后,客户端使用 Refresh Token 向认证服务器请求新的 Access Token。
  3. 认证服务器验证 Refresh Token 的有效性,颁发新的 Access Token(可选颁发新的 Refresh Token)。
  4. 客户端使用新的 Access Token 继续访问资源。

代码实现

Spring Security OAuth2 配置

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {clients.inMemory()
            .withClient("client-id")
            .secret("client-secret")
            .authorizedGrantTypes("password", "refresh_token")
            .scopes("read", "write")
            .accessTokenValiditySeconds(900) // 15 分钟
            .refreshTokenValiditySeconds(604800); // 7 天
    }
}

JWT 签名验证配置

@Bean
public JwtAccessTokenConverter accessTokenConverter() {JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
    converter.setSigningKey("my-secret-key");
    return converter;
}

Refresh Token 存储与校验

推荐使用 Redis 存储 Refresh Token,以下是一个简单的实现:

public class RedisTokenStore implements TokenStore {
    private final RedisTemplate<String, Object> redisTemplate;

    @Override
    public void storeRefreshToken(OAuth2RefreshToken refreshToken, OAuth2Authentication authentication) {redisTemplate.opsForValue().set("refresh_token:" + refreshToken.getValue(),
            authentication,
            refreshToken.getExpiration().getTime() - System.currentTimeMillis(),
            TimeUnit.MILLISECONDS
        );
    }
}

生产环境考量

Token 吊销清单(Blacklist)

实现一个 Token 黑名单,用于在用户登出或 Token 被撤销时立即失效 Token。可以使用 Redis 的过期特性来实现:

public void revokeToken(String token) {redisTemplate.opsForValue().set(
        "blacklist:" + token,
        "revoked",
        getExpirationTime(token) - System.currentTimeMillis(),
        TimeUnit.MILLISECONDS
    );
}

防重放攻击的 Nonce 机制

为了防止 Token 被截获后重放,可以在 JWT 中加入 Nonce(一次性随机数):

public String generateToken(User user) {String nonce = UUID.randomUUID().toString();
    redisTemplate.opsForValue().set("nonce:" + nonce, "used", 15, TimeUnit.MINUTES);
    return Jwts.builder()
        .setSubject(user.getUsername())
        .claim("nonce", nonce)
        .setExpiration(new Date(System.currentTimeMillis() + 900000))
        .signWith(SignatureAlgorithm.HS256, "my-secret-key")
        .compact();}

避坑指南

  • Refresh Token 存储 :Refresh Token 必须安全存储,建议加密后存储在数据库中或使用安全的存储服务。
  • TTL 设计 :避免 Token 无限续期,可以通过限制 Refresh Token 的使用次数或设置绝对过期时间来控制。
  • 性能优化 :跨服务验证 Token 时,可以使用本地缓存来减少对认证服务的压力。

开放性问题

如何平衡 Token 有效期与业务安全等级要求?不同的业务场景(如金融 vs 社交)对安全性和用户体验的需求不同,如何根据业务特点定制 Token 策略?

正文完
 0
评论(没有评论)