4. 鉴权基础概念:认证与授权区别、Token 与 Session、JWT 结构解析

在多租户 SaaS 架构中,鉴权(Authentication & Authorization)是拦截链路的第一道防线。很多开发者混淆了“认证”与“授权”的职责,导致租户隔离出现严重漏洞。本节从最基础的概念出发,厘清认证与授权的本质区别,对比 Token 与 Session 的适用场景,并深入解析 JWT 的结构与安全约束。

4.1 认证 vs 授权:两个截然不同的安全职责

认证(Authentication)回答“你是谁”,授权(Authorization)回答“你能做什么”。在 SaaS 系统中,认证通常由身份提供者(IdP)完成,授权则由策略决策点(PDP)执行。两者必须分离,否则无法实现细粒度的租户权限控制。

维度 认证(Authentication) 授权(Authorization)
核心问题 你是谁? 你能做什么?
输入 凭证(密码、MFA、SSO Token) 身份信息 + 资源标识 + 操作意图
输出 身份声明(Subject / Principal) 允许 / 拒绝决策
存储 用户表、LDAP、OIDC Provider 策略表、RBAC 角色、ABAC 属性
典型协议 OAuth2 授权码流程、SAML2、LDAP Bind OAuth2 Scope、XACML、Casbin
多租户关注点 租户身份源隔离、密码哈希算法 租户级资源权限、跨租户越权防护
实战提示: 在 SaaS 拦截链路中,认证失败应返回 401 Unauthorized,授权失败应返回 403 Forbidden。很多团队错误地将所有鉴权失败统一返回 401,导致客户端无法区分“未登录”与“无权限”,增加调试成本。

4.2 Token 与 Session:两种会话管理范式的对比

Session 和 Token 是两种主流的会话状态管理方式。Session 是“服务端有状态”模型,Token 是“客户端自包含”模型。在分布式多租户 SaaS 中,Token 方案更具扩展性,但需要更严格的安全处理。

特性 Session Token(JWT)
状态存储位置 服务端内存 / Redis 客户端(Token 自身携带)
扩展性 需共享 Session 存储(粘性 Session 或集中缓存) 无状态,任意节点均可验证
跨域支持 受限于 Cookie SameSite 策略 可通过 Authorization Header 自由传递
吊销能力 直接删除服务端 Session 即可 需维护黑名单或缩短有效期
多租户隔离 Session 中存储 tenant_id,需防篡改 JWT 中嵌入 tenant_id,依赖签名防篡改
典型适用场景 单体应用、内网系统 微服务、移动端、跨域 SaaS
安全警告: 使用 Session 时,务必设置 HttpOnly、Secure、SameSite=Lax/Strict 属性,防止 XSS 窃取 Session ID。使用 Token 时,禁止将 Token 明文存储在 localStorage 中,优先使用 httpOnly Cookie 或 Authorization Header + 短有效期 方案。

4.3 JWT 结构解析:Header、Payload、Signature

JWT(JSON Web Token)是目前 SaaS 系统中最常用的自包含 Token 格式。一个完整的 JWT 由三部分组成,以点号(.)分隔:Header.Payload.Signature。下面通过一个实际解码后的 JWT 示例来拆解每一部分。

// 原始 JWT(Base64URL 编码)
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiJ1c2VyXzEyMyIsInRlbmFudF9pZCI6InRlbmFudF9hYmMiLCJyb2xlIjoiYWRtaW4iLCJpYXQiOjE3MDAwMDAwMDAsImV4cCI6MTcwMDAwMzYwMH0.
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQ...

// 解码后的 Header
{
  "alg": "RS256",
  "typ": "JWT"
}

// 解码后的 Payload
{
  "sub": "user_123",
  "tenant_id": "tenant_abc",
  "role": "admin",
  "iat": 1700000000,
  "exp": 1700003600
}

4.3.1 Header(头部)

  • alg:签名算法,常见值 HS256(对称)、RS256(非对称)、ES256。
  • typ:类型,固定为 JWT。
  • kid(可选):密钥 ID,用于多密钥轮换场景。

4.3.2 Payload(载荷)

  • Registered Claims:预定义声明,如 iss(签发者)、sub(主题)、aud(受众)、exp(过期时间)、iat(签发时间)。
  • Public Claims:公开自定义声明,需在 IANA 注册或使用命名空间避免冲突。
  • Private Claims:私有自定义声明,如 tenant_id、role、permissions。

4.3.3 Signature(签名)

签名由 Header 中指定的算法对 Header + "." + Payload 进行加密生成。使用对称算法(HS256)时,服务端和客户端共享同一个密钥;使用非对称算法(RS256)时,服务端持有私钥签名,客户端或第三方服务使用公钥验证。

多租户关键点: 务必在 JWT Payload 中显式包含 tenant_id 声明,并在验证环节强制校验该字段与请求路径/域名中的租户标识一致,防止租户 A 的 Token 被用于访问租户 B 的资源。
JWT 结构解析与签名验证流程 JWT 结构解析与签名验证流程 Header(头部) alg: RS256 typ: JWT kid: key_01(可选) Payload(载荷) sub: user_123 tenant_id: tenant_abc exp: 1700003600 Signature(签名) 算法: RS256 私钥签名 公钥验证 签名生成过程 Base64URL(Header) + "." + Base64URL(Payload) → 使用私钥(RS256)加密 → 生成 Signature 最终 JWT:Header.Payload.Signature(三部分以点号分隔) 验证过程 接收 JWT → 提取 Header 中的 alg → 使用对应公钥解密 Signature → 比对 Hash 是否一致 验证通过后,提取 Payload 中的 tenant_id、role 等声明进行授权决策

4.4 多租户场景下的 JWT 安全约束

在多租户 SaaS 中,JWT 的使用必须遵循以下安全约束,否则极易出现租户越权漏洞:

  1. 强制校验 tenant_id:在 API 网关或拦截器中,将 JWT 中的 tenant_id 与请求路径(如 /api/{tenant_id}/resource)或请求头中的租户标识进行比对,不一致则直接拒绝。
  2. 使用非对称算法(RS256/ES256):避免使用对称算法(HS256),因为对称密钥一旦泄露,攻击者可伪造任意租户的 Token。非对称算法下,服务端私钥不对外暴露,客户端仅持有公钥。
  3. 短有效期 + 刷新机制:Access Token 有效期建议不超过 15 分钟,配合 Refresh Token 实现无感续期。长有效期 Token 会增加泄露后的风险窗口。
  4. 禁止在 Payload 中存储敏感数据:Payload 仅做 Base64URL 编码,并非加密。密码、信用卡号等敏感信息不得放入 JWT。
  5. 黑名单机制:对于需要立即吊销的场景(如用户退出、租户禁用),维护一个 Redis 黑名单,每次验证时检查 JWT 的 jti(JWT ID)是否在黑名单中。
实战案例: 某 SaaS 平台曾因 JWT 中未校验 tenant_id,导致租户 A 的用户通过修改请求路径中的租户 ID,成功访问租户 B 的客户数据。修复方案:在 API 网关层增加 tenant_id 一致性校验,并将该校验逻辑前置到认证过滤器之前。
多租户 JWT 验证拦截链路 多租户 JWT 验证拦截链路 客户端 携带 JWT 请求 API 网关 ① 解析 JWT ② 校验签名 + 过期时间 租户校验过滤器 ③ 提取 tenant_id ④ 比对请求路径/Header 授权决策 ⑤ 允许/拒绝 拒绝:返回 403 Forbidden,记录审计日志 原因:租户不匹配 / 签名无效 / Token 过期 允许:转发请求到后端服务,注入租户上下文 在请求头中添加 X-Tenant-Id: tenant_abc

4.5 常见鉴权漏洞与防御清单

漏洞类型 攻击方式 防御措施
JWT 未校验签名 攻击者修改 Payload 中的 tenant_id,服务端未验证签名即信任 强制校验 JWT Signature,使用非对称算法
Session 固定攻击 攻击者诱导用户使用已知 Session ID 登录后重新生成 Session ID,设置 Secure 和 HttpOnly
Token 泄露 Token 被 XSS 窃取或 HTTPS 中间人截获 使用 httpOnly Cookie 存储 Token,启用 HSTS,短有效期
跨租户越权 使用租户 A 的 Token 访问租户 B 的资源 在拦截链路中强制校验 tenant_id 一致性
算法混淆攻击 将 RS256 改为 HS256,使用公钥作为对称密钥验证 服务端固定允许的算法列表,拒绝非预期算法
推荐实践: 在 SaaS 项目中,建议使用 OAuth2 + JWT(RS256) 作为认证授权基础方案。OAuth2 负责授权码流程和 Token 颁发,JWT 负责自包含的身份与租户声明。API 网关层统一完成 JWT 验证和 tenant_id 校验,后端服务无需重复实现鉴权逻辑。