4. JWT与Token设计:面向多租户的JWT载荷设计
在多租户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签发必须绑定租户上下文。以下是标准签发流程:
- 身份认证:用户通过用户名/密码、OAuth2.0或SSO完成认证。
- 租户选择:用户选择要操作的租户(或系统根据子域名/请求头自动识别)。
- 角色解析:查询该用户在选定租户下的角色,并解析出权限列表。
- Token签发:使用RS256或ES256算法,用私钥签名生成JWT。
- 返回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。
标准刷新流程:
- 客户端携带Refresh Token请求刷新端点。
- 服务端校验Refresh Token签名和有效期。
- 校验Refresh Token是否被吊销(检查黑名单或版本号)。
- 重新查询用户在当前租户下的角色和权限(防止租户内角色变更)。
- 签发新的Access Token和Refresh Token(可选轮换)。
- 返回新Token对,旧Refresh Token失效。
工程建议:使用Refresh Token轮换(Rotation)机制——每次刷新都生成新的Refresh Token,并使旧的Refresh Token立即失效。这可以有效防止Refresh Token泄露后的长期滥用。
4.4 多租户JWT生命周期与鉴权流程
下图展示了从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请求,既保证安全又简化前端鉴权逻辑。