共计 1929 个字符,预计需要花费 5 分钟才能阅读完成。
背景痛点
在分布式系统中,传统的多令牌管理方案(如 access token 和 refresh token 分离)带来了不少问题:

- 维护成本高 :需要同时管理两种令牌的生命周期,增加了系统复杂性
- 安全隐患多 :refresh token 一旦泄露,攻击者可以长期获取新的 access token
- 性能开销大 :每次令牌刷新都需要额外的网络请求和验证
- 用户体验差 :频繁的令牌过期和刷新会中断用户操作
方案对比
| 维度 | All in Token | JWT | OAuth2 |
|---|---|---|---|
| 生命周期管理 | 单一令牌 | 双令牌 | 多令牌 |
| 性能开销 | 低 | 中 | 高 |
| 安全性 | 高 | 中 | 中 |
| 实现复杂度 | 低 | 中 | 高 |
核心实现
HMAC-SHA256 令牌自包含原理
All in Token 的核心思想是将所有必要信息包含在单个令牌中,使用 HMAC-SHA256 进行签名确保安全性。令牌结构如下:
header.payload.signature
- header:包含算法类型和令牌类型
- payload:包含用户信息、权限、有效期等
- signature:使用密钥对前两部分进行签名
令牌生命周期时序图
sequenceDiagram
participant Client
participant Server
Client->>Server: 登录请求
Server->>Server: 生成 All in Token
Server-->>Client: 返回 Token
Client->>Server: 携带 Token 请求
Server->>Server: 验证 Token
alt Token 有效
Server-->>Client: 返回请求数据
else Token 即将过期
Server->>Server: 生成新 Token
Server-->>Client: 返回新 Token
else Token 无效
Server-->>Client: 返回 401
end
代码示例
自定义 Claims 构建
@Data
@Builder
public class TokenClaims {
private String userId;
private List<String> roles;
private Instant issuedAt;
private Instant expiresAt;
public Map<String, Object> toMap() {Map<String, Object> claims = new HashMap<>();
claims.put("userId", userId);
claims.put("roles", roles);
claims.put("iat", issuedAt.getEpochSecond());
claims.put("exp", expiresAt.getEpochSecond());
return claims;
}
}
过滤器链的令牌自动续期逻辑
public class TokenRefreshFilter extends OncePerRequestFilter {
private final TokenService tokenService;
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) {String token = extractToken(request);
if (token != null && tokenService.shouldRefresh(token)) {
// 使用双重检查锁处理并发刷新
synchronized (this) {if (tokenService.shouldRefresh(token)) {String newToken = tokenService.refreshToken(token);
response.setHeader("X-New-Token", newToken);
}
}
}
filterChain.doFilter(request, response);
}
}
生产考量
压测数据
| 密钥长度 | QPS (每秒请求数) |
|---|---|
| 256-bit | 12,000 |
| 512-bit | 8,500 |
| 1024-bit | 5,200 |
安全清单
- 必须验证签名
- 实现令牌黑名单机制
- 定期轮换签名密钥
- 限制令牌有效期
- 记录令牌使用日志
避坑指南
避免令牌膨胀
- 只存储必要的最小信息
- 避免在令牌中存储敏感数据
- 使用简洁的标识符而非完整对象
时钟漂移解决方案
- 所有服务器使用 NTP 同步时间
- 在验证令牌时加入时钟偏差容忍(如 ±30 秒)
- 定期检查服务器时间同步状态
开放问题
如何平衡令牌有效期与吊销列表存储成本?
- 较长的有效期减少吊销列表大小,但增加安全风险
- 较短的有效期增加吊销列表压力,但提高安全性
- 可能的解决方案:
- 分级令牌系统
- 使用概率数据结构(如布隆过滤器)优化存储
- 基于风险的动态有效期调整
正文完
