4. JWT深入解析:JWT结构、签名算法与令牌选型
JWT(JSON Web Token)是现代分布式认证授权体系中最核心的令牌格式。理解JWT的结构、签名机制及其与Opaque Token的差异,是设计高可用、高安全SSO平台的基础。本节将从架构视角拆解JWT的每一层,并给出选型决策依据。
4.1 JWT结构:三段式令牌的构成
一个标准的JWT由三个Base64URL编码的部分组成,以点号(.)分隔:Header.Payload.Signature。每一段都承载着特定的语义和职责。
| 组成部分 | 位置 | 内容 | 是否加密 | 核心作用 |
|---|---|---|---|---|
| Header(头部) | 第一段 | 令牌类型(typ)和签名算法(alg) | 仅Base64编码,明文 | 声明令牌类型和签名算法,供验证方解析 |
| Payload(载荷) | 第二段 | 声明(Claims):注册声明、公共声明、私有声明 | 仅Base64编码,明文 | 携带用户身份、权限、过期时间等业务数据 |
| Signature(签名) | 第三段 | 对Header和Payload的签名值 | 二进制签名数据 | 防篡改、验证令牌真实性 |
架构提示:JWT的Payload是明文可见的,任何拥有令牌的人都可以Base64解码查看内容。因此,绝对不要在Payload中存放密码、密钥、完整手机号等敏感信息。如需保护Payload内容,应使用JWE(JSON Web Encryption)进行加密。
4.1.1 Header(头部)
Header通常包含两个字段:typ(类型)和alg(签名算法)。示例:
{
"alg": "RS256",
"typ": "JWT"
}
- typ:固定为"JWT",表示这是一个JWT令牌。
- alg:签名算法标识,常见值包括HS256、RS256、ES256等。该字段决定了签名验证的数学原理。
4.1.2 Payload(载荷)
Payload包含三类声明(Claims):
| 声明类型 | 说明 | 示例 |
|---|---|---|
| 注册声明 | 预定义的标准字段,如iss(签发者)、sub(主题)、aud(受众)、exp(过期时间)、iat(签发时间) | "exp": 1735689600 |
| 公共声明 | 在IANA注册或使用URI命名的自定义字段,避免冲突 | "https://api.example.com/role": "admin" |
| 私有声明 | 双方约定的自定义字段,不注册,仅内部使用 | "user_id": "u12345" |
安全警告:攻击者可能篡改
exp(过期时间)或iat(签发时间)声明。必须依赖签名验证来确保这些声明未被修改。永远不要信任未经验签的JWT中的任何字段。
4.1.3 Signature(签名)
签名是对Header和Payload的完整性保护。生成过程如下:
signature = sign( base64url(header) + "." + base64url(payload), secret_or_private_key )
验证方使用相同的算法和密钥,对Header和Payload重新计算签名,并与令牌中的签名比对。若一致,则令牌未被篡改。
4.2 签名算法:HS256 vs RS256
签名算法是JWT安全性的基石。HS256和RS256是最常用的两种算法,它们在密钥管理、性能和安全模型上有本质区别。
核心区别:HS256使用对称密钥(同一个密钥既签名又验签),RS256使用非对称密钥对(私钥签名,公钥验签)。
| 对比维度 | HS256(HMAC-SHA256) | RS256(RSA-SHA256) |
|---|---|---|
| 密钥类型 | 对称密钥(单一密钥) | 非对称密钥对(私钥+公钥) |
| 签名方 | 认证服务器(持有密钥) | 认证服务器(持有私钥) |
| 验证方 | 认证服务器(持有密钥) | 任何服务(持有公钥) |
| 密钥分发 | 需安全共享密钥给所有验证方 | 公钥可公开分发,无需保密 |
| 性能 | 签名和验签速度极快 | 签名较慢,验签较快 |
| 密钥轮换 | 需同步更新所有验证方,复杂度高 | 仅需更新认证服务器私钥,公钥可提前发布 |
| 适用场景 | 单体应用、内部微服务(验证方少且可控) | 分布式系统、第三方API、SSO(验证方多且不可控) |
架构避坑:在微服务架构中,如果使用HS256,每个微服务都必须持有相同的对称密钥。一旦某个服务被攻破,密钥泄露将导致整个系统的令牌可被伪造。RS256通过公钥验签,即使公钥泄露,也无法伪造令牌,安全性更高。
4.2.1 HS256 签名与验证流程
// 签名(认证服务器)
signature = HMAC-SHA256( base64url(header) + "." + base64url(payload), shared_secret )
// 验证(认证服务器或持有共享密钥的服务)
expected_signature = HMAC-SHA256( base64url(header) + "." + base64url(payload), shared_secret )
if signature == expected_signature: 验证通过
4.2.2 RS256 签名与验证流程
// 签名(认证服务器,使用私钥)
signature = RSA-SHA256( base64url(header) + "." + base64url(payload), private_key )
// 验证(任何服务,使用公钥)
expected_signature = RSA-SHA256( base64url(header) + "." + base64url(payload), public_key )
if signature == expected_signature: 验证通过
4.3 JWT vs Opaque Token:架构选型决策
Opaque Token(不透明令牌)是另一种常见的令牌格式,通常是一个随机字符串,本身不包含任何用户信息。JWT和Opaque Token各有优劣,选型取决于架构需求。
核心差异:JWT是自包含的令牌,验证方无需调用认证服务器即可验证;Opaque Token是引用型令牌,验证方必须调用认证服务器或令牌内省接口来获取用户信息。
| 对比维度 | JWT(自包含令牌) | Opaque Token(不透明令牌) |
|---|---|---|
| 令牌内容 | 包含用户身份、权限、过期时间等结构化信息 | 随机字符串,无业务含义 |
| 验证方式 | 本地验签,无需网络调用 | 需调用认证服务器的内省(Introspection)接口 |
| 性能 | 高(无网络开销,本地计算) | 低(每次验证都有网络延迟) |
| 可撤销性 | 差(令牌签发后,在过期前无法主动撤销,除非维护黑名单) | 好(服务端可随时删除令牌记录,实现即时撤销) |
| 信息泄露风险 | 高(Payload明文,需避免存放敏感数据) | 低(令牌本身无信息,用户数据在服务端) |
| 令牌大小 | 较大(包含完整Payload) | 小(通常16-32字节) |
| 适用场景 | 高并发、低延迟、无状态API、分布式系统 | 高安全要求、需即时撤销、令牌生命周期短 |
4.3.1 架构选型建议
- 优先选择JWT的场景:
- 微服务架构,服务间需要无状态认证。
- API网关需要快速验证令牌,避免每次转发都调用认证中心。
- 令牌生命周期短(如15-30分钟),撤销需求不强烈。
- 优先选择Opaque Token的场景:
- 需要即时撤销用户会话(如用户主动登出、管理员封禁账号)。
- 令牌生命周期长(如数小时或数天),撤销能力至关重要。
- 令牌中需要携带大量敏感信息,但不想暴露给客户端。
混合架构实践:许多SSO平台采用双令牌模式:使用Opaque Token作为Refresh Token(长生命周期,可撤销),使用JWT作为Access Token(短生命周期,无状态验证)。这样既获得了JWT的高性能,又保留了Opaque Token的撤销能力。
4.4 JWT结构、签名与令牌选型全景图
下图展示了JWT的三段式结构、HS256与RS256的签名验证流程,以及JWT与Opaque Token在架构中的不同角色。
4.5 关键架构决策总结
- 签名算法选型:在分布式SSO架构中,优先选择RS256。它允许每个微服务通过公开的公钥独立验证JWT,无需共享密钥,降低了密钥泄露风险。
- 令牌格式选型:对于高并发、低延迟的API网关和微服务,使用JWT作为Access Token;对于需要即时撤销能力的Refresh Token,使用Opaque Token。
- 安全底线:永远不要在JWT的Payload中存放敏感数据;始终验证签名;使用HTTPS传输令牌;设置合理的过期时间(建议Access Token 15-30分钟)。
架构师视角:JWT不是银弹。它的无状态特性带来了性能优势,但也牺牲了即时撤销能力。在SSO平台设计中,理解JWT的局限性并合理搭配Opaque Token,才能构建出既高性能又高安全的认证授权体系。