JWT 结构深度解析,签名算法:非对称加密 RS256 / 对称加密 HS256 的区别?
·
JWT 结构深度解析,签名算法:非对称加密 RS256 / 对称加密 HS256 的区别?
在使用 JWT(JSON Web Token)进行身份认证时,很多同学有很多问题:
- JWT 到底是一个什么?构造是怎么样的?
- 加密算法,RS256 / HS256 有什么区别?
- 验签到底在验什么?
exp过期是怎么判断的?- Token 过期与失效机制 是怎么样的?
本文将从 原理 → 实现 → 安全 → 实战设计 的角度,系统性地把这两个问题讲清楚。
一、JWT 的基本结构回顾
一个标准的 JWT 由三部分组成:
header.payload.signature
------------------------------
eyJhbGciOiJ9.eyJzdWIiOjM5MDIyfQ.SflKxwRJJf36POssw5c
- Header(头部):声明使用的算法(如 RS256 / HS256)
- Payload(载荷):业务数据(用户信息、过期时间等)
- Signature(签名):防篡改签名(安全核心)
- Header:
作用:描述令牌的元数据和签名算法。
内容:通常包含令牌类型(typ: “JWT”)和签名算法(alg,如 HS256、RS256)。
存储形式:一个 JSON 对象,经过 Base64Url 编码后形成第一部分。
示例解码后:
{
"alg": "HS256",
"typ": "JWT"
}
- Payload
作用:包含实际的声明(claims),即要传递的信息。
内容:
1.预定义声明:标准字段,如 iss(签发者)、exp(过期时间)、sub(主题)等。
2.公共声明:可以自定义的字段,但为了避免冲突,应使用已定义的名字或在名字中包含唯一标识符。
3.私有声明:在双方约定下自定义的字段,用于交换信息。
存储形式:一个 JSON 对象,经过 Base64Url 编码后形成第二部分。
示例解码后:
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
- Signature
作用:验证令牌的真实性和完整性,防止数据被篡改。这是 JWT 安全性的核心。
生成方式:将编码后的 Header 和 Payload 用 . 连接起来,然后使用 Header 中指定的算法和一个密钥(secret)进行签名。
公式(以 HS256 为例):
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)
验证方式:服务器收到 Token 后,会用同样的密钥(如果是非对称加密,使用的是公钥)和算法重新计算签名,并与 Token 中的第三部分进行比较。如果一致,说明 Token 未被篡改;如果不一致或签名验证失败,则 Token 无效。
其中:
- Base64Url 编码 ≠ 加密:Header 和 Payload 只是进行了编码,任何人都可以解码并查看其内容。因此,绝对不要在 Payload 中存放敏感信息(如密码)。
- 签名是关键:虽然内容可见,但由于有签名的存在,任何对 Header 或 Payload 的修改都会导致签名验证失败,从而使 Token 作废。
- 所以Signature 才是用来防止伪造和篡改的关键
二、HS256 与 RS256 的核心区别
1. HS256:对称加密(HMAC + SHA-256)
原理
signature = HMAC_SHA256(
header.payload,
secretKey
)
- 使用 同一个密钥
- 既负责 签名,也负责 验签
特点
- 实现简单
- 性能高
- 适合单体应用或内部系统
安全隐患(重点)
任何拿到 secretKey 的服务,都可以伪造 Token
在微服务或第三方校验场景中,这是不可接受的。
2. RS256:非对称加密(RSA + SHA-256)
原理
私钥(Private Key) → 签名
公钥(Public Key) → 验签
signature = RSA_SHA256(
header.payload,
privateKey
)
特点
- 私钥只存在于认证中心
- 公钥可下发给所有资源服务
- 验签方无法伪造 Token
为什么更安全?
即使公钥泄露,也无法反推出私钥
因此:
- OAuth2
- 单点登录
- 微服务架构
几乎全部使用 RS256。
3. HS256 vs RS256 对比总结
| 对比项 | HS256 | RS256 |
|---|---|---|
| 密钥类型 | 对称密钥 | 非对称密钥 |
| 密钥数量 | 1 个 | 2 个(公钥 / 私钥) |
| 是否适合微服务 | ❌ | ✅ |
| 是否支持安全密钥轮换 | 困难 | 容易 |
| 安全边界 | 较弱 | 强 |
| 性能 | 更快 | 稍慢 |
三、JWT 验签流程到底在做什么?
很多人会把「验签」和「是否有效」混为一谈,其实这是两个完全不同的阶段。
验签做的事情只有一件:
1. 取出 header.payload
2. 使用约定的算法 + 服务端密钥
3. 重新计算 signature'
4. 与 token 中的 signature 对比
结果只有两种:
- ❌ 不一致 → Token 被篡改(直接无效)
- ✅ 一致 → 只能说明 Token 是“真的”
⚠️ 注意:
验签通过 ≠ Token 还能用
四、Token 的「失效」是怎么判断的?
关键结论
JWT 的失效不是“算出来的”,而是“读出来的”
失效信息在 Payload 里。
五、最核心的失效字段:exp
1. exp 是什么?
{
"sub": "10086",
"iat": 1700000000,
"exp": 1700003600
}
exp:expiration time(过期时间,秒级时间戳)
2. 判断逻辑
if (now > exp) {
token 已过期
}
重要认知
exp不影响签名是否正确- 它只是 payload 中的一个字段
- 所以会出现:
| 情况 | 结果 |
|---|---|
| 签名正确 + exp 未过期 | Token 有效 |
| 签名正确 + exp 已过期 | Token 失效 |
| 签名错误 | Token 无效 |
六、完整的 Token 有效性判断流程(生产级)
真实系统中,一个请求的 JWT 校验流程是:
1️⃣ 校验签名(防伪)
2️⃣ 解析 payload
3️⃣ 校验 exp(是否过期)
4️⃣ 校验 nbf / iat(可选)
5️⃣ 业务层校验(是否被吊销、版本是否匹配)
只要 任意一步失败:
→ SecurityContext 不会建立
→ 返回 401 / 403
七、除了 exp,还有哪些失效手段?
1. nbf(Not Before)
"nbf": 1700001000
含义:
在该时间之前,Token 不允许被使用
2. 服务端主动让 Token 失效(非常常见)
JWT 是无状态的,但业务不是。
常见方案:
方案一:Token 黑名单(Redis)
- 登出 / 封禁
- 把 token / jti 存入 Redis
- 每次请求校验
方案二:用户版本号
{
"uid": 1,
"ver": 3
}
- 数据库中版本是 4
- Token 中是 3
- 判定失效
八、Access Token + Refresh Token 的设计
推荐实践:
Access Token:15 分钟
Refresh Token:7 天
- Access Token 过期 → 走刷新流程
- Refresh Token 过期 → 重新登录
这也是 JWT 能做到“无状态 + 可控失效”的关键。
九、和 Spring Security 的整体执行链路
在 Spring Security 中,真实流程是:
请求进入
→ JWT Filter
→ 校验签名
→ 校验 exp
→ 校验业务状态
→ 构造 Authentication
→ 放入 SecurityContext
→ @AuthenticationPrincipal 生效
十、总结一句话记住
🔐 签名解决的是「有没有被伪造」
⏰ exp 解决的是「现在还能不能用」
🧠 业务校验解决的是「我还认不认你」
结语
- HS256:适合简单、封闭系统
- RS256:生产环境首选,安全边界清晰
- JWT 有效 ≠ 验签通过
更多推荐




所有评论(0)