3. 鉴权体系基础:OAuth2.0、JWT、Session vs Token、多租户下的Token颁发

在多租户SaaS架构中,鉴权是隔离租户数据、保障系统安全的第一道防线。本章将深入剖析现代鉴权体系的四大核心组件:OAuth2.0授权框架、JWT令牌结构、Session与Token的对比,以及多租户场景下的Token颁发策略。理解这些基础,是后续构建CI/CD流水线中鉴权拦截与回滚策略的前提。

3.1 OAuth2.0 授权框架

OAuth2.0 是一个授权框架,而非认证协议。它允许第三方应用获取对用户资源的有限访问权限,而无需暴露用户密码。在SaaS场景中,OAuth2.0常用于API网关与微服务之间的委托授权。

核心角色:
  • 资源所有者(Resource Owner): 通常是最终用户,拥有数据访问权限。
  • 客户端(Client): 需要访问资源的第三方应用(如SaaS前端)。
  • 授权服务器(Authorization Server): 负责验证资源所有者身份并颁发令牌。
  • 资源服务器(Resource Server): 托管受保护资源的服务,通过验证令牌来响应请求。

OAuth2.0 定义了四种授权模式,其中授权码模式(Authorization Code Grant) 是最常用且最安全的模式,适用于有后端的Web应用。在CI/CD流水线中,我们通常关注客户端凭证模式(Client Credentials Grant),用于服务间(M2M)通信。

OAuth2.0 授权码模式核心流程 OAuth2.0 授权码模式流程 用户(资源所有者) 客户端(前端) 授权服务器 资源服务器 ① 访问请求 ② 重定向至授权服务器 ③ 返回授权码 ④ 用授权码换取令牌 ⑤ 返回 Access Token ⑥ 携带 Token 访问资源 ⑦ 验证并返回数据 💡 在CI/CD流水线中,服务间调用通常使用“客户端凭证模式”,跳过用户授权步骤,直接由客户端向授权服务器申请Token。 该模式无需用户交互,适合自动化部署和微服务通信场景。

3.2 JWT(JSON Web Token)

JWT 是一种紧凑的、URL安全的令牌格式,用于在各方之间以JSON对象形式安全地传输信息。它通常用于认证和信息交换。JWT 由三部分组成,用点号(.)分隔:

部分 名称 内容说明
Header 头部 包含令牌类型(typ: JWT)和签名算法(alg: HS256 / RS256)。
Payload 载荷 包含声明(Claims),如用户ID、角色、过期时间(exp)、发行者(iss)等。多租户场景下,通常包含 tenant_id 声明。
Signature 签名 对Header和Payload进行签名,防止数据被篡改。使用密钥(HMAC)或公私钥对(RSA/ECDSA)生成。
JWT 最佳实践:
  • 使用短过期时间(TTL): Access Token 建议设置为 15-30 分钟,降低泄露风险。
  • 使用 Refresh Token: 配合长期有效的 Refresh Token 实现无感刷新,避免频繁登录。
  • 避免在 Payload 中存储敏感信息: JWT 的 Payload 仅做 Base64 编码,并非加密,敏感数据应加密后传输。
  • 签名算法优先选择 RS256: 使用非对称加密,资源服务器只需持有公钥即可验证,私钥由授权服务器保管。

3.3 Session vs Token

Session 和 Token 是两种主流的会话管理方式。在微服务架构和多租户SaaS中,Token(尤其是JWT)已成为事实标准。下表从多个维度对比两者的差异:

对比维度 Session(服务端会话) Token(如 JWT)
存储位置 服务端内存/Redis,客户端仅存 Session ID 客户端存储(LocalStorage / Cookie),服务端无状态
扩展性 差。需要共享 Session 存储(如 Redis),增加架构复杂度 好。无状态,任意服务节点均可验证,天然支持水平扩展
跨域支持 困难。受限于 Cookie 同源策略 容易。Token 可通过 HTTP Header 携带,支持跨域请求
安全性 Session ID 泄露风险,但服务端可主动销毁 Token 泄露后,在过期前无法撤销(除非维护黑名单)
多租户适配 需在 Session 中手动注入租户上下文 天然支持。将 tenant_id 编码在 Token 中,随请求传递
性能 每次请求需查询 Session 存储,增加 I/O 开销 服务端仅需验证签名,计算开销低,性能更优
注意: 虽然 Token 方案在扩展性和多租户支持上优势明显,但 Token 的“不可撤销性”是一个关键挑战。在CI/CD流水线中,当检测到异常或需要回滚时,必须配合 Token 黑名单 或 短 TTL + Refresh Token 轮换 策略来快速阻断访问。

3.4 多租户下的Token颁发

在多租户SaaS中,Token 颁发不再是简单的用户认证,还需要解决租户隔离和租户路由问题。核心设计原则是:Token 中必须携带租户上下文,且该上下文在 Token 生命周期内不可篡改。

3.4.1 租户上下文注入方式

在 JWT 的 Payload 中,我们通过自定义声明(Custom Claims)来携带租户信息。常见的做法是添加 tenant_id 字段。对于多角色或多组织的用户,可能还需要 org_id 或 role 字段。

// JWT Payload 示例(多租户场景)
{
  "sub": "user_12345",
  "tenant_id": "tenant_acme_corp",
  "role": "admin",
  "iss": "https://auth.saas-platform.com",
  "exp": 1718000000,
  "iat": 1717996400
}

3.4.2 颁发流程设计

多租户下的 Token 颁发流程需要区分用户登录和服务间调用两种场景:

  • 用户登录场景: 用户在登录页面输入凭证,授权服务器验证身份后,查询用户所属的租户列表(可能多个),让用户选择当前会话的租户上下文,然后颁发包含选定 tenant_id 的 Token。
  • 服务间调用场景(M2M): 客户端使用 Client ID 和 Client Secret 向授权服务器申请 Token。授权服务器根据客户端注册时绑定的租户信息,自动颁发包含对应 tenant_id 的 Token。
多租户Token颁发与验证流程 多租户Token颁发与验证流程 客户端/用户 授权服务器 用户/租户数据库 资源服务器 ① 登录请求 ② 查询用户及租户信息 ③ 返回租户列表 ④ 颁发含 tenant_id 的 JWT ⑤ 携带 JWT 访问资源 ⑥ 验证签名 & 提取 tenant_id 🔑 关键点:资源服务器在验证Token后,从Payload中提取 tenant_id,并将其注入到当前请求的 线程上下文(ThreadLocal)中,后续所有数据库查询和业务逻辑都基于该租户ID进行数据隔离。

3.4.3 颁发策略对比

策略 描述 适用场景 优缺点
单租户Token 一个Token仅对应一个租户,用户切换租户需重新登录或刷新Token。 用户只属于一个租户,或SaaS平台限制用户同时只能操作一个租户。 ✅ 实现简单,隔离性强
❌ 用户体验差,切换租户成本高
多租户Token(含租户列表) Token的Payload中包含用户可访问的租户ID列表,请求时通过Header指定当前租户。 用户属于多个租户,需要频繁切换上下文(如管理控制台)。 ✅ 灵活,用户体验好
❌ Token体积变大,需额外校验当前租户是否在列表中
无状态Token + 租户路由 Token中仅包含用户ID,租户信息由API网关根据请求域名或路径动态解析。 租户与域名/子域名强绑定(如 tenant1.saas.com)。 ✅ Token体积最小,网关层统一处理
❌ 增加网关复杂度,租户路由逻辑需高可用
CI/CD 流水线中的实践建议:
  • 在流水线的集成测试阶段,为每个租户创建独立的测试账号,并预生成包含特定 tenant_id 的测试Token。
  • 在部署回滚策略中,如果新版本引入了鉴权漏洞,应立即通过授权服务器吊销所有已颁发的Token(维护黑名单),并强制用户重新登录。
  • 使用密钥轮换(Key Rotation)机制,定期更换JWT签名密钥。在CI/CD中,密钥轮换应作为自动化任务,与部署流水线联动。