共计 2958 个字符,预计需要花费 8 分钟才能阅读完成。
1. 为什么需要 Activity Token
在传统的单体应用中,Session 管理相对简单,服务器可以轻松维护用户状态。但随着微服务架构的普及,这种模式遇到了明显瓶颈:

- 跨服务状态同步难题 :用户请求可能在多个服务间跳转,每个服务都需要验证身份
- 扩展性限制 :Session 存储往往依赖于特定服务器内存,无法水平扩展
- 跨域障碍 :前后端分离架构下,Cookie-based 的 Session 存在跨域限制
Activity Token 应运而生,它本质上是一种自包含的凭证,将验证所需信息直接编码在 Token 中,服务端无需维护状态。
2. 技术选型对比
| 方案 | 状态管理 | 传输方式 | 适用场景 |
|---|---|---|---|
| Session | 有状态 | Cookie | 简单单体应用 |
| JWT | 无状态 | Header/URL | 一次性验证场景 |
| OAuth2 | 混合 | 多种 | 第三方授权 |
| Activity Token | 轻状态 | Authorization 头 | 服务间通信 |
Activity Token 的独特优势在于:
- 服务间友好 :专为内部服务通信优化,比 JWT 更轻量
- 可控生命周期 :支持动态刷新机制,比 Session 更灵活
- 最小化存储 :只需要缓存最新 Token 状态,不像 Session 需要全量存储
3. 核心实现细节
3.1 Token 生成算法
推荐采用 HMAC-SHA256 签名方案,平衡安全性与性能:
def generate_token(user_id, secret_key):
header = {
"alg": "HS256",
"typ": "ACTIVITY"
}
payload = {
"uid": user_id,
"iat": int(time.time()),
"exp": int(time.time()) + 3600 # 1 小时有效期
}
encoded_header = base64url_encode(json.dumps(header))
encoded_payload = base64url_encode(json.dumps(payload))
signature = hmac.new(secret_key.encode(),
f"{encoded_header}.{encoded_payload}".encode(),
hashlib.sha256
).digest()
return f"{encoded_header}.{encoded_payload}.{base64url_encode(signature)}"
3.2 验证逻辑关键点
- 结构校验 :检查三段式结构是否完整
- 签名验证 :重新计算签名比对
- 时效检查 :验证 iat 和 exp 时间戳
- 吊销检查 :查询 Redis 黑名单
3.3 刷新机制设计
采用双 Token 方案提升安全性:
- Access Token:短期有效(1 小时),直接用于业务请求
- Refresh Token:长期有效(7 天),仅用于获取新 Access Token
刷新流程示例:
public TokenPair refreshTokens(String refreshToken) {
// 验证 refresh token 有效性
if (!tokenService.validateRefreshToken(refreshToken)) {throw new InvalidTokenException();
}
// 获取用户身份
String userId = tokenService.parseUserId(refreshToken);
// 生成新 token 对
return new TokenPair(generateAccessToken(userId),
generateRefreshToken(userId)
);
}
4. 生产级实现示例
4.1 Spring Boot 集成
@RestController
public class TokenController {@PostMapping("/token")
public ResponseEntity<?> createToken(@RequestBody AuthRequest request) {
// 1. 验证用户凭证
User user = authService.authenticate(request);
// 2. 生成 token 对
TokenPair tokens = tokenService.generateTokenPair(user.getId());
// 3. 记录 refresh token
tokenStore.saveRefreshToken(user.getId(), tokens.getRefreshToken());
return ResponseEntity.ok(tokens);
}
@GetMapping("/protected")
public ResponseEntity<?> protectedResource(@RequestHeader("Authorization") String authHeader) {
// 1. 提取并验证 token
String token = authHeader.substring(7); // Bearer 前缀
if (!tokenService.validateToken(token)) {return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();}
// 2. 获取业务数据
String userId = tokenService.parseUserId(token);
return ResponseEntity.ok(dataService.getSecureData(userId));
}
}
4.2 Redis 存储优化
使用 Hash 结构存储 Token 元数据:
# Key 设计
user:tokens:{user_id}
├── access_token : "token_value"
├── expires_at : 1654783999
└── last_refresh : 1654780399
集群环境下建议:
- 启用 Redis 持久化(AOF+RDB)
- 设置合理的 TTL 自动清理
- 使用 Redisson 客户端实现分布式锁
5. 安全防护策略
5.1 常见攻击防御
| 攻击类型 | 防御措施 |
|---|---|
| 重放攻击 | 使用 nonce 值或请求指纹 |
| CSRF | 校验 Origin 头 +Double Submit Cookie |
| 令牌窃取 | 绑定 IP/ 设备指纹 + 短期有效期 |
5.2 关键配置建议
- 过期时间 :Access Token 不超过 1 小时,Refresh Token 建议 7 天
- 密钥管理 :使用 KMS 轮换,禁止硬编码
- 传输安全 :强制 HTTPS,禁止 URL 传输
6. 进阶设计思路
根据业务需求扩展 Token Claims:
{
"uid": "user123",
"roles": ["admin", "auditor"],
"perms": ["data:read", "report:write"],
"tenant": "acme-corp"
}
但需注意:
- 敏感信息不要直接暴露在 Token 中
- 控制 Payload 大小(建议 <1KB)
- 变更权限后需要重新签发
实践心得
在实际项目中落地 Activity Token 时,建议采用渐进式策略:
- 先从内部服务间通信开始试点
- 建立完善的监控指标(签发量、验证失败率等)
- 定期进行安全审计(检查密钥强度、日志泄露等)
随着系统规模扩大,可以考虑升级到更复杂的零信任架构,但 Activity Token 作为轻量级方案,在大多数场景下仍能提供优秀的安全与性能平衡。
正文完
