一、Token 是什么?------本质与原理
Token 是由认证服务签发的一段加密/编码字符串,用于在后续请求中证明客户端的身份和权限。
- 它通常以
Authorization: Bearer <token>的形式随 HTTP 请求头传递。 - 主流格式是 JWT(JSON Web Token) ,结构为:
Header.Payload.Signature。- Header:声明签名算法(如 RS256)、Token 类型。
- Payload :包含标准声明(
iss,exp,sub)和自定义声明(userId,roles,tenantId)。 - Signature:对前两部分进行 Base64Url 编码后,用密钥签名,确保不可篡改。
✅ 核心特性 :自包含(Self-contained) + 无状态(Stateless)
服务端无需存储会话,仅需验证签名即可信任 Token 内容。
二、为什么要使用 Token?------微服务架构下的必然选择
1. 无状态性(Stateless)
- 微服务强调服务自治、水平扩展。若使用 Session,需共享存储(如 Redis),增加耦合与运维复杂度。
- Token 自包含用户信息,任意服务节点均可独立验证,天然契合微服务。
2. 跨域与多端支持
- Web、App、IoT 设备等可通过统一 Header 携带 Token,不依赖 Cookie,避免 CORS 和 CSRF 问题。
3. 解耦认证与业务
- 可引入独立 认证中心(Auth Server)(如 Keycloak、Auth0、自研 OAuth2 服务),业务服务只负责验证 Token。
- 符合"关注点分离"原则,提升系统可维护性与安全性。
4. 细粒度权限控制
- Token Payload 可嵌入角色、权限、租户 ID 等,服务端据此做 RBAC/ABAC 控制,无需每次查 DB。
三、Token 如何运作?------端到端流程(含网关集成)
在大型微服务架构中,API 网关(如 Spring Cloud Gateway、Kong、Nginx)是 Token 处理的第一道防线。
典型架构流程:
[Client]
↓ (1) 登录 → /auth/login
[Auth Service] ← 验证凭证 → 生成 JWT + Refresh Token
↓ (2) 返回 tokens
[Client]
↓ (3) 调用业务 API → Header: Authorization: Bearer <access_token>
[API Gateway]
↓ (4) 验证 JWT 签名 & exp
├─ 无效 → 401 Unauthorized
└─ 有效 → (5) 转发请求 + 透传用户上下文(如 userId)
[Business Service]
↓ (6) 基于 Token 中 claims 做权限校验(如 @PreAuthorize("hasRole('ADMIN')"))
网关(Gateway)中的关键处理:
1. Token 验证(Verification)
- 网关配置公钥(RS256)或共享密钥(HS256),使用 Nimbus JOSE 或 Spring Security 验证 JWT。
- 检查
exp(过期时间)、iss(签发者)、aud(受众)等标准声明。
2. 上下文透传(Context Propagation)
-
网关解析 Token 后,将关键信息(如
userId,roles)写入请求头:httpX-User-Id: 12345 X-Roles: admin,user -
下游服务无需再次解析 JWT,直接读取 Header 即可,提升性能。
3. 路由级权限控制(可选)
-
网关可基于 Token 中的角色,控制是否允许访问某条路由(如
/admin/**仅限 admin)。 -
示例(Spring Cloud Gateway + Spring Security):java
@Bean public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) { http.authorizeExchange(exchanges -> exchanges.pathMatchers("/admin/**").hasRole("ADMIN") .anyExchange().authenticated() ); }
⚠️ 注意:网关只做认证(你是谁),不做细粒度授权(你能做什么)。后者应由业务服务完成。
四、实际开发中的关键处理 ------ 续期、安全与工程实践
1. 双 Token 机制:Access Token + Refresh Token
| Token 类型 | 有效期 | 存储方式 | 用途 |
|---|---|---|---|
| Access Token | 短(5~30m) | 内存 / HttpOnly Cookie | 访问受保护资源 |
| Refresh Token | 长(7~30d) | HttpOnly + Secure Cookie / 安全存储 | 获取新 Access Token |
刷新流程:
- 客户端发现 Access Token 过期(收到 401)。
- 携带 Refresh Token 调用
/auth/refresh。 - Auth Service 验证 Refresh Token 合法性(存在、未过期、未吊销、上下文匹配)。
- 颁发新 Access Token(可选新 Refresh Token)。
- 若 Refresh Token 无效 → 返回 401,强制重新登录。
2. Refresh Token 安全设计
- 一次性使用(One-time Use):每次使用后生成新 Refresh Token,旧 Token 立即失效。
- 绑定设备/上下文:记录首次使用的 IP、User-Agent,后续刷新需匹配。
- 持久化存储 :数据库存储
{token, userId, deviceId, issuedAt, revoked}。 - 吊销机制 :提供
/logout接口,将 Refresh Token 标记为 revoked。
📌 示例场景:用户点击"退出所有设备",系统批量吊销该用户的所有 Refresh Tokens。
3. Access Token "登出"难题与解决方案
JWT 无法主动失效(因其无状态),但可通过以下方式缓解:
- 短期有效期(如 15 分钟):降低泄露窗口。
- Token 黑名单:将已登出但未过期的 Token 加入 Redis(TTL = 剩余有效期)。
- 用户版本号(Version Claim) :
- JWT Payload 中加入
v=2; - 用户登出时,DB 中
user.token_version++; - 网关验证时比对 Token 中的
v与 DB 中的token_version,不一致则拒绝。
- JWT Payload 中加入
4. Java 工程实践建议
-
技术栈:
- 网关:Spring Cloud Gateway + Spring Security OAuth2 Resource Server
- JWT 库:Nimbus JOSE + JWT(Spring Security 默认)
- 认证中心:Keycloak / Auth0 / 自研(基于 Spring Authorization Server)
-
关键配置(网关验证 JWT):
spring: security: oauth2: resourceserver: jwt: jwk-set-uri: https://auth.example.com/.well-known/jwks.json -
性能优化:
- 网关缓存公钥(JWKS),避免频繁拉取;
- 避免在每个业务服务重复解析 JWT,由网关统一处理并透传上下文。
五、高级面试问题延伸
Q1:网关验证 Token 失败,应该返回 401 还是 403?
答:401(Unauthorized)表示"未认证"(Token 无效/缺失);403(Forbidden)表示"已认证但无权限"。网关只负责认证,故应返回 401。
Q2:如何防止 Token 被盗用?
答:
- 强制 HTTPS;
- Token 存于 HttpOnly + Secure Cookie(防 XSS);
- 敏感操作(如转账)要求二次认证;
- 实施风控:异常 IP、设备切换触发 re-login。
Q3:OAuth2 与 JWT 的关系?
答:OAuth2 是授权框架,定义了 Token 的使用流程(如授权码模式);JWT 是 Token 的一种具体格式。OAuth2 的 access_token 可以是 JWT(结构化、自包含),也可以是 opaque string。
总结
"在大型 Java 微服务架构中,Token(尤其是 JWT)是实现无状态认证授权的核心。我们通过独立认证中心签发短期 Access Token 和长期 Refresh Token。API 网关作为统一入口,负责验证 Token 签名、检查有效期,并将用户上下文透传至下游服务。Refresh Token 采用一次性、上下文绑定、持久化存储策略,确保安全续期。对于登出等场景,结合用户版本号或短期 TTL 弥补 JWT 无状态的不足。整个体系基于 Spring Security 构建,兼顾安全性、性能与可扩展性。"