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