4. JWT与Token设计

在多租户SaaS架构中,JWT(JSON Web Token)不仅是身份凭证,更是跨服务传递租户上下文的核心载体。设计不当的载荷会导致租户隔离失效、权限越界或Token风暴。本节从载荷结构、签发策略、刷新机制三个维度,给出面向多租户的工程化方案。

4.1 多租户JWT载荷结构设计

标准JWT载荷(Payload)包含注册声明(iss、sub、exp等)和公共声明。多租户场景下,必须显式声明以下三个自定义字段:

字段 类型 说明 示例值
tenant_id string 当前请求所属租户的唯一标识 "t-abc123"
role string 用户在当前租户下的角色 "admin"
permissions string[] 角色解析后的权限编码列表(用于快速鉴权) ["read:order","write:order"]
设计原则:权限列表(permissions)应在签发Token时由角色静态解析完成,避免每次请求都查询数据库。对于超大规模租户(权限数超过50个),建议改用权限位图或角色Hash,以减小Token体积。

一个完整的JWT载荷示例:

{
  "sub": "user_7890",
  "tenant_id": "t-abc123",
  "role": "workspace_admin",
  "permissions": [
    "read:workspace",
    "write:workspace",
    "read:member",
    "invite:member",
    "delete:member"
  ],
  "iat": 1710000000,
  "exp": 1710003600,
  "iss": "https://auth.saas.example.com"
}
避坑:不要在JWT中存储完整用户信息(如手机号、邮箱、地址)。Token在客户端和网络传输中可能被截获,敏感信息应通过OAuth2.0的UserInfo端点按需获取。

4.2 Token签发策略

多租户场景下,Token签发必须绑定租户上下文。以下是标准签发流程:

  1. 身份认证:用户通过用户名/密码、OAuth2.0或SSO完成认证。
  2. 租户选择:用户选择要操作的租户(或系统根据子域名/请求头自动识别)。
  3. 角色解析:查询该用户在选定租户下的角色,并解析出权限列表。
  4. Token签发:使用RS256或ES256算法,用私钥签名生成JWT。
  5. 返回Token:同时返回Access Token和Refresh Token。
关键决策:Access Token的有效期建议设置为15~30分钟;Refresh Token有效期建议为7~30天。对于金融、医疗等合规要求高的场景,Refresh Token应支持轮换(Rotation)和过期回收。

签发策略对比表:

策略 Access Token有效期 Refresh Token有效期 适用场景
标准策略 15分钟 7天 通用SaaS应用
长会话策略 30分钟 30天 低风险、高用户体验要求
短生命周期策略 5分钟 1天 金融、医疗、高安全要求

4.3 Token刷新策略

刷新流程需要额外校验租户上下文是否变更。当用户切换租户时,必须重新签发Token,而非直接刷新旧Token。

标准刷新流程:

  1. 客户端携带Refresh Token请求刷新端点。
  2. 服务端校验Refresh Token签名和有效期。
  3. 校验Refresh Token是否被吊销(检查黑名单或版本号)。
  4. 重新查询用户在当前租户下的角色和权限(防止租户内角色变更)。
  5. 签发新的Access Token和Refresh Token(可选轮换)。
  6. 返回新Token对,旧Refresh Token失效。
工程建议:使用Refresh Token轮换(Rotation)机制——每次刷新都生成新的Refresh Token,并使旧的Refresh Token立即失效。这可以有效防止Refresh Token泄露后的长期滥用。

4.4 多租户JWT生命周期与鉴权流程

下图展示了从Token签发、请求鉴权到刷新的完整链路:

多租户JWT生命周期与鉴权流程 多租户JWT生命周期与鉴权流程 1. 用户登录 2. 签发JWT 3. 携带Token请求 4. 鉴权拦截器 5. 校验通过 6. Token过期 7. 刷新Token 8. 重新签发 说明: Token过期后,客户端自动使用Refresh Token刷新,无需用户重新登录。刷新时重新校验租户角色和权限。

4.5 多租户Token安全最佳实践

实践项 说明 推荐等级
使用非对称算法(RS256/ES256) 私钥签名,公钥验证,避免密钥泄露导致全网Token可伪造 强制
Token绑定客户端指纹 在JWT中加入客户端设备指纹或IP段,防止Token被盗用 强烈推荐
Refresh Token轮换 每次刷新生成新Refresh Token,旧Token立即失效 强烈推荐
Token黑名单 用户登出或管理员踢人时,将Token加入Redis黑名单 推荐
JWT体积控制 权限列表超过20个时,改用角色Hash或权限位图 推荐
安全红线:永远不要在JWT中存储密码、密钥、完整手机号等敏感信息。JWT仅用于身份验证和授权上下文传递,敏感数据应通过服务端API按需获取。

4.6 多租户Token存储与传输

客户端Token存储方案对比:

存储方式 安全性 XSS防护 CSRF防护 推荐场景
HttpOnly Cookie 高 强(不可被JS读取) 需额外CSRF Token 服务端渲染应用
内存变量 高 强 天然防护 SPA + BFF架构
localStorage 低 弱(可被XSS读取) 天然防护 不推荐生产环境
sessionStorage 中 弱(可被XSS读取) 天然防护 临时页面场景
工程建议:对于现代SPA应用,推荐使用BFF(Backend For Frontend)模式,将Token存储在BFF层的HttpOnly Cookie中,前端通过BFF代理API请求,既保证安全又简化前端鉴权逻辑。