JWT
先给你一句总纲,先稳住:
✅ HS256:对称签名;RS256:非对称(RSA)签名,你这句是对的!
⚠️ 注意:不管 HS256 / RS256,Header 和 Payload 永远只是 Base64URL 编码,不是加密!编码方式【写死是Base64URL】,Header里面只写【签名算法】,不写编码方式!
编码 = 换一种写法,随时还原,人人看得懂;加密 = 糊成乱码,要有密钥才能解开。
JWT 全程固定编码:Base64URL(是Base64的一个变种,把+ / =改掉,适合放URL、请求头里面)
一、先拆分【生成JWT全过程】,一步一步(分 HS256 举例,再对比RS256)
JWT结构:【header串】.【payload串】.【signature签名串】
第1步:写 Header(JSON)
{
"alg": "HS256",
"typ": "JWT"
}
alg:签名算法!这里写 HS256 或者 RS256typ:固定写JWT,代表这是JWT
👉 然后:把这个JSON,做 Base64URL编码 → 得到第一段字符串
第2步:写 Payload(JSON,存业务信息)
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
👉 同样:这个JSON,直接 Base64URL编码 → 第二段字符串
❗重点:这里没有加密!就是单纯转成Base64URL,随便谁拿到,都可以还原出这个JSON!Header里不会写Base64,这个编码是JWT协议规定死的。
第3步:算签名(最容易迷糊的地方来了!)
把前面两段拼起来:base64Header.base64Payload
然后根据Header里写的alg,走不同签名逻辑:
✅ 如果 alg = HS256(对称签名,HMAC-SHA256)
公式:
签名 = HMAC-SHA256( "第一段.第二段", 同一个Secret密钥 )
- HMAC 是什么:拿【拼接字符串 + 共享密钥】丢进SHA256算出一个指纹(带锁的指纹)
- 这个Secret,后端存好,前端不能拿到这个Secret!前端只存完整JWT字符串!
算完这个结果,再转Base64URL,就是第三段签名。
✅ 如果 alg = RS256(非对称签名,RSA-SHA256)
公式:
签名 = RSA私钥签名( SHA256("第一段.第二段") , 私钥 )
流程拆开:
- 先对
第一段.第二段算 SHA256 摘要(普通指纹) - 用服务器自己的RSA私钥,对这个指纹签名盖章
私钥:只有服务器自己攥着,绝不外传;配套公钥可以公开出去给其他服务验签
👉 最后三段用.拼起来,就是一长串JWT,发给前端。前端存起来(localStorage/cookie)
📌 一句话区分两个签名:
HS256:一把共享密码secret,算签名、验签名都用这同一个密码
RS256:一对密钥,私钥用来生成签名,公钥只用来验签名
二、后端收到前端带过来的JWT之后:验证流程!
前端每次请求,把整串JWT放在请求头带过来。后端拿到 A.B.C
- 先按
.切成三块:A(编码后的header)、B(编码后的payload)、C(签名串) - 把A做Base64URL解码 → 拿到Header JSON,读出里面
alg,看是HS256还是RS256 - 重新计算一遍预期签名:
- 如果是HS256:拿
A.B+ 后端本地保存的Secret,重新跑一遍HMAC-SHA256,算出【新签名】 - 如果是RS256:拿
A.B先算SHA256摘要,拿公钥去校验C这个签名是不是这个摘要盖出来的
- 如果是HS256:拿
- 对比校验:
- HS256:新算出来的签名 和 传过来的C 一模一样 ✅ 校验通过
- RS256:公钥校验签名合法 ✅ 校验通过
- 校验成功之后:才把B做Base64URL解码,拿到Payload里面用户信息,鉴权放行
⚠️ 关键点:校验签名,是先校验,再解码拿payload!
如果你自己改了payload里面的name、admin=true,B就变了,A.B跟着变,后端重新算签名就对不上,直接拒绝。
三、把你刚才一堆疑问单独拎出来逐个回答
- Q:Payload里面是编码?是Base64吗?
A:是,Base64URL(Base64变体),协议固定,Header里不写这个编码方式。Header只写签名算法(HS256/RS256) - Q:Header里面写的是加密算法?
A:❌ 准确说:签名算法!JWT这里没有加密业务数据,只有签名。HS256叫对称签名算法,不是加密算法。 - Q:签名那边是把Header和Payload拼起来算东西?
A:✅ 对,把编码后的第一段、第二段,中间加个点拼在一起,再根据alg的算法算出签名,作为第三段 - Q:前端拿到之后做什么?
A:前端啥都不用算!只负责存、每次请求带上这整串字符串,不参与签名、验签! secret/私钥前端拿不到!
四、极简对比 HS256 vs RS256
| 项目 | HS256(HMAC-SHA256) | RS256(RSA-SHA256) |
|---|---|---|
| 类型 | 对称签名 | 非对称签名 |
| 密钥 | 同一个Secret,签名验签都用它 | 私钥签名,公钥验签(一对) |
| 适合场景 | 单体项目,就这一个服务发token、验token | 微服务、第三方登录,多个服务验签,公钥分发 |
五、一个超级容易踩的坑再强调
很多人混淆:
Base64URL = 编码(JWT Header、Payload用这个)
SHA256 = 哈希摘要(算指纹)
HMAC-SHA256(HS256) = 对称签名
RSA-SHA256(RS256) = 非对称签名
===== 签发 JWT =====
# a 是编码后的Header.编码后的Payload
a = base64URL(Header) + "." + base64URL(Payload)
if alg == HS256:
sign_raw = HMAC-SHA256(a, secret)
sign = base64URL(sign_raw)
elif alg == RS256:
digest = SHA256(a)
sign_raw = RSA_SIGN(digest, RSA_pri)
sign = base64URL(sign_raw)
token = a + "." + sign # 也就是 A.B.C,发给前端
===== 校验 JWT =====
A,B,C = token.split(".")
a = A + "." + B
if alg == HS256:
expected_raw = HMAC-SHA256(a, secret)
expected_C = base64URL(expected_raw)
if expected_C == C: ✅通过
else: ❌失败
elif alg == RS256:
digest = SHA256(a)
# ==========这里是唯一要改的地方!==========
sign_raw = base64URL_decode(C)
ok = RSA_VERIFY(digest, sign_raw, RSA_pub)
if ok == true: ✅通过
else: ❌失败

浙公网安备 33010602011771号