共计 2850 个字符,预计需要花费 8 分钟才能阅读完成。
背景痛点
在微服务架构中,Agent Token 作为服务间通信的凭证,其管理和验证面临着两大核心挑战:

-
性能瓶颈 :传统基于数据库的 Token 验证方式,每次请求都需要查询数据库进行校验,在高并发场景下会导致数据库压力骤增,成为系统性能的瓶颈。
-
安全风险 :Token 泄露、重放攻击、密钥管理不当等问题,都可能引发严重的安全漏洞,威胁整个系统的安全。
技术方案
为了解决上述问题,我们提出了一套基于 JWT、Redis 缓存和动态刷新机制的解决方案。
1. JWT+ 非对称加密实现自包含验证
JWT(JSON Web Token)是一种自包含的 Token,其 payload 部分可以携带用户信息,签名部分则用于验证 Token 的合法性。采用非对称加密(如 RS256)可以避免密钥泄露的风险。
选型理由 :
- RS256 vs HS256:HS256 使用对称加密,密钥需要共享,存在泄露风险;RS256 使用非对称加密,私钥用于签名,公钥用于验证,安全性更高。
2. Redis 缓存验证结果降低数据库压力
将验证过的 Token 及其对应的用户信息缓存到 Redis 中,后续请求可以直接从 Redis 中获取验证结果,避免频繁查询数据库。
3. 动态刷新机制设计
为了平衡安全性和用户体验,我们引入了 Refresh Token 机制:
- Access Token:有效期较短(如 15 分钟),用于日常请求。
- Refresh Token:有效期较长(如 7 天),用于获取新的 Access Token。
代码示例
JWT 生成 / 验证工具类
public class JwtUtils {
private static final String ISSUER = "your-issuer";
private static final String AUDIENCE = "your-audience";
private static final long ACCESS_TOKEN_EXPIRE_TIME = 15 * 60 * 1000; // 15 分钟
private static final long REFRESH_TOKEN_EXPIRE_TIME = 7 * 24 * 60 * 60 * 1000; // 7 天
public static String generateAccessToken(String subject, Map<String, Object> claims) {return Jwts.builder()
.setSubject(subject)
.setIssuer(ISSUER)
.setAudience(AUDIENCE)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + ACCESS_TOKEN_EXPIRE_TIME))
.addClaims(claims)
.signWith(SignatureAlgorithm.RS256, getPrivateKey())
.compact();}
// 其他方法省略...
}
Redis 缓存集成代码
@RestController
@RequestMapping("/api")
public class AuthController {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginRequest request) {
// 验证用户 credentials
String accessToken = JwtUtils.generateAccessToken(userId, claims);
String refreshToken = JwtUtils.generateRefreshToken(userId);
// 将 Token 缓存到 Redis
redisTemplate.opsForValue().set("token:" + userId, accessToken, 15, TimeUnit.MINUTES);
redisTemplate.opsForValue().set("refresh:" + userId, refreshToken, 7, TimeUnit.DAYS);
return ResponseEntity.ok(new AuthResponse(accessToken, refreshToken));
}
}
防重放攻击的 Nonce 机制实现
public class NonceUtils {
private static final long NONCE_EXPIRE_TIME = 5 * 60 * 1000; // 5 分钟
public static boolean checkNonce(String nonce) {
// 检查 nonce 是否已存在
if (redisTemplate.opsForValue().get("nonce:" + nonce) != null) {return false;}
// 将 nonce 存入 Redis
redisTemplate.opsForValue().set("nonce:" + nonce, "1", NONCE_EXPIRE_TIME, TimeUnit.MILLISECONDS);
return true;
}
}
性能对比
我们进行了基准测试,对比了数据库验证和 Redis 缓存验证的性能:
| 验证方式 | QPS | 平均响应时间 |
|---|---|---|
| 数据库验证 | 1,200 | 25ms |
| Redis 缓存验证 | 15,000 | 2ms |
从测试结果可以看出,Redis 缓存验证的 QPS 是数据库验证的 12 倍以上,平均响应时间也显著降低。
安全实践
密钥管理方案
- 私钥:存储在安全的密钥管理服务(如 AWS KMS、HashiCorp Vault)中,定期轮换。
- 公钥:可以通过 API 或配置文件分发给各个微服务。
Token 吊销策略
- 用户登出时,将 Token 加入黑名单(Redis 中设置过期时间)。
- 定期清理过期的黑名单 Token。
日志审计要点
- 记录所有 Token 的生成、验证和吊销操作。
- 监控异常登录行为(如频繁登录失败、异地登录等)。
避坑指南
时钟漂移问题处理
由于 JWT 的验证依赖于系统时间,时钟漂移可能导致 Token 验证失败。解决方案:
- 所有服务器使用 NTP 服务同步时间。
- 在验证 Token 时,允许一定的时间偏差(如 5 分钟)。
分布式环境下的缓存一致性方案
- 使用 Redis 的 pub/sub 机制,在 Token 吊销时通知所有节点更新缓存。
- 设置适当的缓存过期时间,避免长期不一致。
过期的 Token 清理策略
- 对于 Redis 中的 Token,设置合理的 TTL,让其自动过期。
- 对于数据库中的 Token,可以定期运行清理任务,删除过期的记录。
结尾
通过 JWT、Redis 缓存和动态刷新机制的结合,我们成功解决了微服务架构下 Agent Token 管理的性能和安全问题。然而,Token 的有效期设置仍然是一个需要权衡的问题:过短会影响用户体验,过长会增加安全风险。大家在实际项目中是如何平衡这一点的呢?欢迎在评论区分享你的经验!
