大家好,我是苏三,又跟大家见面了。
最近有球友问我:微服务中Token鉴权除了使用JWT之外,还有什么其他的方案?
今天这篇文章跟大家一起聊聊微服务Token鉴权的7种方案,希望对会有所帮助。
传统Session的致命缺陷:
多个服务无法共享Session。
重复认证,导致系统性能严重下降。
2023年某电商平台发送安全事故:
GET /api/users/balance HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIn0.Gfx6VO9tcxwk6xqx9yYzSfebbeKDTHkQKh0xhu4nJE0
黑客通过XSS攻击窃取此Token后,在2小时内盗取5万用户余额,暴露三大漏洞:
该方案适合初创系统。
核心架构:
致命陷阱:
// 错误示例:未校验Token有效性
public Claims parseJwt(String token) {
return Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody(); // 若Token被注销仍能解析通过!
}
正确实现:
// 结合Redis校验Token状态
public boolean validateToken(String token, UserDetails details) {
String username = extractUsername(token);
String redisToken = redisTemplate.opsForValue().get("token:"+username);
// 双重验证:签名有效且未注销
return (username.equals(details.getUsername())
&& !isTokenExpired(token)
&& token.equals(redisToken);
}
适用场景:用户量<100万的中小型系统
该方案是第三方接入的首选。
OAuth2.0包含了4种授权模式:
授权码模式流程:
Spring Boot配置示例:
spring:
security:
oauth2:
client:
registration:
github:
client-id:${GITHUB_CLIENT_ID}
client-secret:${GITHUB_SECRET}
scope:user:email,read:user
provider:
github:
token-uri:https://github.com/login/oauth/access_token
user-info-uri:https://api.github.com/user
关键点:必须使用PKCE扩展防止授权码截持攻击
该方案是的国产Token鉴权方案的精品。
三大核心优势:
// 登录
StpUtil.login(10001);
// 鉴权
@SaCheckPermission("user:delete")
public void deleteUser(Long id) {
// 业务代码
}
// 查询所有会话
List<String> sessionList = StpUtil.searchSessionId("user:*", 0, 10);
// 根据账号ID踢人
StpUtil.kickout(10001);
// 根据Token值踢人
StpUtil.kickoutByTokenValue("xxxx");
网关集成方案:
@Bean
public SaReactorFilter saReactorFilter() {
return new SaReactorFilter()
.addInclude("/**")
.setAuth(obj -> {
SaRouter.match("/user/**").check(r -> StpUtil.checkPermission("USER"));
SaRouter.match("/admin/**").check(r -> StpUtil.checkPermission("ADMIN"));
});
}
性能实测:QPS 12,000(Redis集群模式)
该方案是微服务的标配。
架构设计:
响应式鉴权过滤器:
public class AuthFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 1. 提取Token
String token = extractToken(exchange.getRequest());
// 2. 响应式鉴权调用
return reactiveAuthService.validateToken(token)
.flatMap(valid -> {
if (!valid) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
});
}
}
性能优化技巧:
该方案适合服务链调用。
核心问题:服务A调用服务B时Token如何传递
解决方案:
Feign中继实现:
@FeignClient(name = "service-b")
public interface ServiceBClient {
@GetMapping("/data")
Data getData(@RequestHeader("Authorization") String token);
}
// 调用方
public Data getData(String token) {
// 原样传递Token
return serviceBClient.getData("Bearer " + token);
}
安全加固:使用JWT嵌套加密防止内部Token泄露
该方案能保证金融级安全。
与JWT的核心区别:
Java生成示例:
public String createJwe(User user) throws JOSEException {
// 1. 组装Header
JWEHeader header = new JWEHeader.Builder(JWEAlgorithm.A256GCMKW,
EncryptionMethod.A256GCM).build();
// 2. 创建Payload
Payload payload = new Payload(new JSONObject()
.put("sub", user.getId())
.put("ssn", encrypt(user.getSsn()))); // 敏感信息加密
// 3. 加密Token
JWEObject jwe = new JWEObject(header, payload);
jwe.encrypt(new AESEncrypter(SECRET_KEY.getBytes()));
return jwe.serialize();
}
适用场景:
该方案是零信任架构。
工作流程:
Spring Boot配置:
server:
ssl:
key-store:classpath:server-keystore.p12
key-store-password:changeit
key-alias:server
client-auth:need# 关键配置
trust-store:classpath:client-truststore.p12
trust-store-password:changeit
适用场景:
方案 | 平均延时 | CPU消耗 | 安全等级 | 适用场景 |
---|---|---|---|---|
基础JWT | 3ms | 15% | ★★☆ | 内部微服务 |
OAuth2.0 | 35ms | 40% | ★★★☆ | 第三方开放平台 |
Sa-Token | 5ms | 18% | ★★★ | 快速开发项目 |
网关统一鉴权 | 8ms | 25% | ★★★☆ | 多语言混合架构 |
Token中继 | 12ms | 30% | ★★★ | 服务链调用 |
JWE加密 | 45ms | 60% | ★★★★☆ | 金融敏感数据 |
mTLS | 20ms | 50% | ★★★★★ | 零信任网络 |
测试环境:AWS c5.4xlarge 16核32GB × 3节点
攻击类型 | 防御方案 | 代码实现 |
---|---|---|
Token窃取 | 绑定设备指纹 | StpUtil.getToken().setExtra("deviceId", fingerprint) |
重放攻击 | Nonce校验+时间戳 | redis.opsForValue().setIfAbsent(nonce, "used", 5, TimeUnit.SECONDS) |
越权访问 | 动态权限校验 | @SaCheckPermission("#user.id") |
Token破解 | 定期轮换签名密钥 | Jwts.parserBuilder().setSigningKeyResolver(new KeyRotationResolver()) |
为了保证系统的操作安全,我们需要增加审计日志表。
审计日志必备字段如下:
public class AuditLog {
private String tokenId; // Token唯一标识
private String userId; // 用户ID
private String operation; // 操作类型
private String resource; // 访问资源
private String clientIp; // 客户端IP
private String deviceInfo; // 设备信息
private LocalDateTime time; // 操作时间
}
微服务安全如同城堡防御—— 单一的护城河无法阻挡所有入侵, 需要城墙、箭塔、卫兵的多层防护。 没有绝对安全的系统,只有不断提高的攻击成本。