微服务架构中的 Token 工作机制详解
引言
在微服务架构中,用户身份认证和授权是系统安全的核心挑战。由于服务被拆分成多个独立的微服务,传统的基于 Session 的认证方式无法满足分布式环境的需求。Token 机制(特别是 JWT)成为微服务架构中实现无状态身份认证的主流方案。本文将深入解析 Token 在微服务中的完整生命周期和工作机制。
一、Token 的生成与前端存储
1.1 Token 的生成时机
Token 在用户登录成功后生成,具体流程如下:
- 用户提交凭证:用户通过前端提交用户名和密码到认证服务
- 凭证验证:认证服务验证用户信息(查询数据库或调用用户服务)
- Token 生成:验证通过后,认证服务生成包含用户身份信息的 Token
- 返回前端:认证服务将 Token 返回给前端
常见的 Token 类型:
- JWT:自包含令牌,包含用户信息、签名和过期时间
- UUID令牌:随机字符串,需与后端存储的用户信息关联
1.2 前端 Token 存储机制
前端收到 Token 后,需要妥善存储并在后续请求中自动携带:
// 登录成功后存储 Token
const login = async (credentials) => {
const response = await fetch('/api/auth/login', {
method: 'POST',
body: JSON.stringify(credentials)
});
const { token } = await response.json();
// 存储到 localStorage 或 sessionStorage
localStorage.setItem('auth_token', token);
// 或设置 Cookie(自动携带)
document.cookie = `auth_token=${token}; path=/; max-age=86400`;
};
// 为所有请求自动添加 Token
axios.interceptors.request.use(config => {
const token = localStorage.getItem('auth_token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
二、Token 在微服务间的传递机制
2.1 API 网关:统一入口和认证
微服务架构通常通过 API 网关作为统一入口:
# Spring Cloud Gateway 配置示例
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://user-service
predicates:
- Path=/api/users/**
filters:
- name: AuthFilter
# 网关统一认证,验证 Token 有效性
网关负责:
- 验证 Token 签名和有效期
- 路由请求到正确的微服务
- 传递原始请求头(包括 Token)
2.2 服务间调用的 Token 自动传递
2.2.1 Feign 客户端的 Token 拦截器
@Component
public class FeignTokenInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
// 从当前线程的 UserContext 获取 Token
UserInfo userInfo = UserContext.get();
if (userInfo != null && userInfo.getToken() != null) {
template.header("Authorization", "Bearer " + userInfo.getToken());
}
}
}
2.2.2 RestTemplate 的 Token 拦截器
@Configuration
public class RestTemplateConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
restTemplate.getInterceptors().add((request, body, execution) -> {
UserInfo userInfo = UserContext.get();
if (userInfo != null && userInfo.getToken() != null) {
request.getHeaders().add("Authorization",
"Bearer " + userInfo.getToken());
}
return execution.execute(request, body);
});
return restTemplate;
}
}
2.3 Token 传递的完整链条
前端 → 网关 → 服务A → 服务B → 服务C
↓ ↓ ↓ ↓ ↓
Token Token Token Token Token
↓ ↓ ↓ ↓ ↓
携带 验证并 从请求头 从请求头 从请求头
转发 提取并 提取并 提取并
存储到 存储到 存储到
TL TL TL
三、微服务内的用户上下文管理
3.1 UserContext 与 ThreadLocal 设计
/**
* 用户上下文管理类
*/
public class UserContext {
private static final ThreadLocal<UserInfo> currentUser = new ThreadLocal<>();
public static void set(UserInfo user) {
currentUser.set(user);
}
public static UserInfo get() {
return currentUser.get();
}
public static void clear() {
currentUser.remove();
}
// 便捷方法
public static Long getUserId() {
UserInfo user = get();
return user != null ? user.getUserId() : null;
}
public static String getUsername() {
UserInfo user = get();
return user != null ? user.getUsername() : null;
}
}
/**
* 用户信息封装类
*/
@Data
public class UserInfo {
private Long userId;
private String username;
private String token;
private List<String> roles;
private List<String> permissions;
// 其他用户相关信息
}
3.2 请求拦截器:Token 解析与上下文设置
@Component
public class AuthenticationInterceptor implements HandlerInterceptor {
@Autowired
private JwtTokenProvider tokenProvider;
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) {
// 从请求头提取 Token
String token = extractToken(request);
if (token != null && tokenProvider.validateToken(token)) {
// 解析 Token 获取用户信息
UserInfo userInfo = tokenProvider.getUserInfoFromToken(token);
userInfo.setToken(token);
// 设置到当前线程的 UserContext
UserContext.set(userInfo);
} else {
// Token 无效,返回 401
response.setStatus(HttpStatus.UNAUTHORIZED.value());
return false;
}
return true;
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler, Exception ex) {
// 请求完成后清理 ThreadLocal,防止内存泄漏
UserContext.clear();
}
private String extractToken(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7);
}
return null;
}
}
3.3 业务代码中的用户信息获取
@RestController
@RequestMapping("/api/orders")
public class OrderController {
@Autowired
private OrderService orderService;
@GetMapping("/my-orders")
public ResponseEntity<List<Order>> getUserOrders() {
// 直接从 UserContext 获取当前用户信息
Long userId = UserContext.getUserId();
String username = UserContext.getUsername();
List<Order> orders = orderService.getOrdersByUserId(userId);
return ResponseEntity.ok(orders);
}
}
@Service
public class OrderService {
public List<Order> getOrdersByUserId(Long userId) {
// 服务层也可以直接获取用户上下文
String currentUser = UserContext.getUsername();
log.info("用户 {} 查询订单,目标用户ID: {}", currentUser, userId);
// 业务逻辑...
return orderRepository.findByUserId(userId);
}
}
四、特殊场景处理
4.1 异步调用中的 Token 传递
@Service
public class AsyncService {
@Async
public CompletableFuture<String> asyncProcess() {
try {
// 手动传递 Token 到异步线程
String token = AsyncContext.getToken();
if (token != null) {
UserInfo userInfo = parseToken(token); // 解析 Token
UserContext.set(userInfo);
}
// 异步业务逻辑
String result = doHeavyProcessing();
return CompletableFuture.completedFuture(result);
} finally {
UserContext.clear(); // 清理上下文
}
}
}
/**
* 异步上下文传递工具类
*/
public class AsyncContext {
private static final ThreadLocal<String> asyncToken = new ThreadLocal<>();
public static void setToken(String token) {
asyncToken.set(token);
}
public static String getToken() {
return asyncToken.get();
}
public static void clear() {
asyncToken.remove();
}
}
// 在调用异步方法前设置 Token
@Service
public class OrderService {
@Autowired
private AsyncService asyncService;
public void processOrderAsync(Order order) {
// 从当前上下文获取 Token 并设置到异步上下文
String token = UserContext.get().getToken();
AsyncContext.setToken(token);
// 调用异步方法
asyncService.asyncProcess();
// 清理异步上下文
AsyncContext.clear();
}
}
4.2 网关层面的统一认证与 Token 中继
@Component
public class GatewayAuthFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
// 提取 Token
String token = extractToken(request);
if (token != null) {
// 验证 Token(可选:可转发到认证服务验证)
if (validateToken(token)) {
// 将用户信息添加到请求头,传递给下游服务
ServerHttpRequest mutatedRequest = request.mutate()
.header("X-User-Id", extractUserId(token))
.header("X-Username", extractUsername(token))
.build();
return chain.filter(exchange.mutate().request(mutatedRequest).build());
}
}
// Token 无效,返回 401
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
// 其他工具方法...
}
五、安全最佳实践
5.1 Token 安全配置
@Configuration
public class JwtConfig {
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration}")
private Long expiration;
@Bean
public JwtTokenProvider jwtTokenProvider() {
return new JwtTokenProvider(secret, expiration);
}
}
@Component
public class JwtTokenProvider {
private final String secret;
private final Long expiration;
public JwtTokenProvider(String secret, Long expiration) {
this.secret = secret;
this.expiration = expiration;
}
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
claims.put("userId", userDetails.getUserId());
claims.put("roles", userDetails.getRoles());
return Jwts.builder()
.setClaims(claims)
.setSubject(userDetails.getUsername())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + expiration))
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}
public boolean validateToken(String token) {
try {
Jwts.parser().setSigningKey(secret).parseClaimsJws(token);
return true;
} catch (Exception e) {
return false;
}
}
}
5.2 防范常见安全威胁
- 使用 HTTPS 防止 Token 被窃听
- 设置合理的过期时间 减少 Token 泄露风险
- 实现 Token 刷新机制 平衡安全性与用户体验
- 使用 HttpOnly Cookie 防范 XSS 攻击
六、完整工作流程总结
- 生成阶段:用户登录 → 认证服务生成 Token → 返回前端存储
- 传递阶段:前端携带 Token → 网关验证 → 服务间自动传递
- 上下文阶段:各服务解析 Token → 设置 ThreadLocal → 业务使用
- 清理阶段:请求完成 → 清理 ThreadLocal → 释放资源
结论
微服务架构中的 Token 工作机制通过"传递+解析+上下文管理"的模式,实现了分布式的无状态认证。关键在于:
- 标准化传递:通过 HTTP 头跨服务一致传递
- 自动化管理:通过拦截器自动处理 Token 传递和解析
- 线程级隔离:通过 ThreadLocal 实现请求级别的用户上下文隔离
- 安全加固:通过签名、过期时间等措施保证 Token 安全性
这种机制既保证了微服务架构的松散耦合特性,又提供了统一的身份认证方案,是现代分布式系统的重要基础设施。

浙公网安备 33010602011771号