3. 鉴权基础:从 Session 到 JWT 的演进、Token 的结构与生命周期管理、常见鉴权漏洞
在多租户 SaaS 架构中,鉴权(Authentication)是拦截链路的第一道关卡。本章从传统 Session 鉴权的瓶颈出发,分析 JWT 的演进动因、Token 结构设计与生命周期管理策略,并梳理常见鉴权漏洞及其防护要点。
3.1 从 Session 到 JWT:鉴权模型的演进
传统单体应用广泛采用 Session-Cookie 鉴权模式:用户登录后,服务端创建 Session 并返回 Session ID 写入 Cookie;后续请求携带 Cookie,服务端查询内存或 Redis 中的 Session 数据完成身份识别。
| 对比维度 | Session 鉴权 | JWT 鉴权 |
|---|---|---|
| 状态存储 | 服务端存储(内存/Redis) | 客户端存储(Token 自包含) |
| 扩展性 | 需共享 Session 存储(粘性 Session / 集中缓存) | 无状态,任意节点均可验证 |
| 跨域支持 | 受 Cookie 同源策略限制 | 通过 Authorization 头,天然跨域 |
| 性能开销 | 每次请求需查询存储 | 本地解析验证,无远程 I/O |
| 多租户隔离 | 需额外设计租户上下文绑定 | Token 内嵌租户标识,天然隔离 |
演进动因:微服务化与多租户 SaaS 要求鉴权层无状态、可水平扩展、支持跨服务传递用户身份。JWT(JSON Web Token)通过自包含的声明结构,将用户身份与权限信息编码进 Token,服务端只需验证签名即可信任 Token 内容,无需回查存储。
3.2 Token 的结构与生命周期管理
3.2.1 JWT 的结构
JWT 由三个 Base64URL 编码的部分组成,以点号(.)分隔:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
| 部分 | 内容 | 示例字段 |
|---|---|---|
| Header | 签名算法 & Token 类型 | {"alg":"HS256","typ":"JWT"} |
| Payload | 声明(Claims):用户身份、租户 ID、角色、过期时间等 | {"sub":"user_001","tenant":"acme","role":"admin","exp":1710000000} |
| Signature | 对 Header + Payload 的签名,防止篡改 | HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret) |
多租户最佳实践:在 Payload 中显式声明 tenant_id 字段,鉴权拦截器解析后注入请求上下文,实现租户级数据隔离。避免将敏感信息(如密码、完整手机号)放入 Payload,因为 Payload 仅 Base64 编码,并非加密。
3.2.2 Token 生命周期管理
JWT 的无状态特性带来便利,但也使 Token 撤销变得困难。合理的生命周期管理策略是鉴权安全的关键:
- 短期 Access Token + 长期 Refresh Token:Access Token 有效期短(15~30 分钟),Refresh Token 有效期长(7~30 天),通过 Refresh Token 轮换获取新 Access Token。
- Token 黑名单(Deny List):在 Redis 中维护已撤销 Token 的
jti(JWT ID)列表,拦截器每次验证时检查黑名单。 - Token 轮换(Rotation):每次使用 Refresh Token 时,同时下发新的 Refresh Token,旧 Refresh Token 立即失效。
- 绝对过期与滑动过期:Access Token 设置绝对过期时间;Refresh Token 可设置滑动过期(每次使用刷新有效期)。
3.3 常见鉴权漏洞与防护
| 漏洞类型 | 攻击方式 | 防护措施 |
|---|---|---|
| Token 泄露 | XSS 窃取 LocalStorage 中的 Token;中间人攻击截获 Token | 使用 HttpOnly Cookie 存储 Token;强制 HTTPS;设置短有效期 |
| 签名算法混淆 | 攻击者将 alg 改为 none 或对称算法,伪造 Token |
服务端强制校验 alg 白名单;拒绝 none 算法 |
| 重放攻击 | 截获合法 Token 后重复使用 | 引入 jti + 一次性使用;结合时间戳与 nonce |
| 弱密钥 | 使用简单密钥,被暴力破解签名 | 使用 256 位以上随机密钥;定期轮换密钥 |
| Token 未过期 | Token 永久有效,泄露后长期可用 | 设置合理的 exp 声明;实现 Refresh Token 轮换 |
| 越权访问 | Token 中未校验租户 ID,A 租户用户访问 B 租户数据 | Token 内嵌 tenant_id;拦截器强制校验租户归属 |
多租户鉴权红线:Token 中的 tenant_id 必须在签发时绑定,且不允许客户端修改。拦截器在解析 Token 后,需将 tenant_id 与请求路径/参数中的租户标识进行比对,防止租户越权。任何绕过租户校验的鉴权实现,都可能导致严重的数据泄露事故。
3.4 多租户 SaaS 鉴权拦截链路设计要点
- 无状态优先:优先采用 JWT 自包含鉴权,减少对集中缓存的依赖,降低拦截链路延迟。
- 租户上下文传递:在 Token Payload 中声明
tenant_id、role、permissions,拦截器解析后注入 ThreadLocal 或请求上下文。 - 分层校验:网关层校验 Token 签名与过期时间;业务层校验租户权限与资源归属。
- 黑名单缓存:对已撤销 Token 的
jti使用 Redis 缓存,设置与 Token 过期时间一致的 TTL,避免内存泄漏。 - 性能监控:对鉴权拦截链路增加耗时埋点,监控 P99 延迟,及时发现签名验证或黑名单查询的性能瓶颈。