聊聊微服务架构中的双token
在微服务架构中,双Token机制(Access Token + Refresh Token)是保障分布式系统认证与授权的核心方案。以下从原理、实现、安全策略及典型场景展开详细说明:
一、核心原理与角色分工
1. 双Token定义
-
Access Token(访问令牌)
- 功能:用于直接访问受保护的微服务资源,类似“临时通行证”。
- 特性:
- 有效期短(如15分钟至2小时),降低泄露风险。
- 包含用户ID、权限、过期时间等基础信息,通过JWT格式自包含存储。
- 前端存储于
LocalStorage
或HttpOnly Cookie
,每次请求携带至Authorization
头。
-
Refresh Token(刷新令牌)
- 功能:用于在Access Token过期时获取新令牌,避免用户重复登录。
- 特性:
- 有效期长(如7天至30天),但非永久有效。
- 后端存储于Redis或数据库,不返回前端以防止篡改。
- 仅用于调用刷新接口,无法直接访问资源。
2. 交互流程
-
初次认证
用户登录后,认证中心生成双Token:- Access Token返回给前端,用于后续API调用。
- Refresh Token存储于Redis,关联用户ID。
-
正常请求流程
前端携带Access Token访问微服务,网关或资源服务验证其有效性:- 有效:直接处理请求。
- 过期:返回
401 Unauthorized
,携带Refresh Token Required
标识。
-
自动续期流程
前端检测到Access Token过期后,使用Refresh Token调用刷新接口:- 认证中心验证Refresh Token有效性,生成新的双Token。
- 前端更新本地Access Token,继续处理原请求。
-
登出与失效
用户登出或检测到风险时,认证中心删除Redis中的Refresh Token,同时前端清空本地存储。
二、微服务架构下的实现要点
1. 认证中心设计
- 独立服务:作为微服务中的授权服务器,负责双Token的生成、刷新和撤销。
- 技术选型:
- 使用Spring Security OAuth2或Keycloak构建认证中心,支持JWT生成与Redis存储。
- 提供统一接口:
/oauth/token
(登录)、/oauth/refresh
(刷新)、/oauth/revoke
(撤销)。
2. 服务间Token传递
-
Feign拦截器:
在调用下游微服务时,通过Feign拦截器将用户的Access Token传递至请求头,解决服务间授权问题。@Component public class FeignRequestInterceptor implements RequestInterceptor {@Overridepublic void apply(RequestTemplate template) {HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();String token = request.getHeader("Authorization");template.header("Authorization", token);} }
-
客户端模式:
服务间无用户上下文时(如认证中心调用权限服务),使用客户端模式生成临时Token,避免循环依赖。
3. 并发刷新处理
- 分布式锁:
当多个请求同时触发Refresh Token刷新时,通过Redis的SETNX
实现分布式锁,确保原子性。String lockKey = "refresh_lock:" + userId; if (redisTemplate.opsForValue().setIfAbsent(lockKey, "1", 5, TimeUnit.SECONDS)) {try {// 生成新Token并更新Redis} finally {redisTemplate.delete(lockKey);} }
4. 跨语言支持
- 统一协议:
不同语言开发的微服务通过OAuth 2.0或OpenID Connect协议集成认证中心。例如:- Java服务使用Spring Security OAuth2 Adapter。
- Python服务使用Flask-OAuthlib或Django OAuth Toolkit。
三、安全策略与最佳实践
1. 风险控制
-
Refresh Token绑定:
- 绑定用户IP或设备指纹,验证时比对一致性。
- 限制刷新频率(如每小时最多5次),防止暴力破解。
-
HTTPS强制:
所有通信使用HTTPS,防止Token在传输中被截获。
2. 存储优化
-
Redis持久化:
使用Redis的String
类型存储Refresh Token,设置过期时间并开启AOF/RDB持久化,保障高可用性。 -
黑名单机制:
对已撤销的Access Token维护短期黑名单(如10分钟),防止重放攻击。
3. 权限细分
-
动态权限控制:
Access Token仅包含必要权限,敏感操作(如支付)生成一次性短时效Token(5分钟)。 -
角色差异化策略:
普通用户使用Redis自动续期,管理员采用双Token机制并强制二次验证刷新。
四、典型场景与对比
1. 单点登录(SSO)
-
方案:
多个子系统共享认证中心的双Token,用户登录一次可访问所有授权资源。 -
优势:
减少重复认证,提升用户体验;集中管理用户权限,降低维护成本。
2. 移动端应用
-
方案:
Access Token有效期设为30分钟,Refresh Token存储于设备本地,后台定期轮询刷新。 -
挑战:
需处理设备丢失场景下的Token撤销,可通过绑定设备ID实现。
3. 对比单Token机制
维度 | 双Token机制 | 单Token机制 |
---|---|---|
安全性 | 高(Access Token短时,Refresh Token隔离存储) | 低(Token泄露风险高) |
用户体验 | 优(无感知刷新) | 差(过期需重新登录) |
扩展性 | 支持多服务、多端集成 | 难以实现SSO和跨端授权 |
复杂度 | 高(需管理双Token生命周期) | 低(单一Token管理) |
五、总结
双Token机制通过短时访问+长效刷新的设计,在微服务架构中实现了安全性与用户体验的平衡。其核心在于:
- 职责分离:Access Token专注资源访问,Refresh Token专注会话延续。
- 分布式协作:认证中心与资源服务通过Token传递和Redis存储实现无状态交互。
- 动态安全策略:结合HTTPS、分布式锁、权限细分等手段,应对复杂攻击场景。
实际应用中,需根据业务需求选择技术栈(如Spring Cloud + JWT或Keycloak),并通过压力测试验证Token刷新性能与并发处理能力。