关于JWT令牌结构的技术咨询及新手参考文档需求
刚接触JWT的时候,我对着[HEADER].[PAYLOAD].[SIGNATURE]这个结构一脸懵,索性把最困惑的几个问题整理出来,结合实际示例拆解清楚,方便和我一样的新手快速上手。
经典JWT结构示例
经典JWT由三个Base64Url编码的部分用点号连接而成,以下是完整的结构拆解:
1. HEADER(头部)
原始JSON内容:
{"alg": "HS256", "typ": "JWT"}
Base64Url编码后:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
2. PAYLOAD(负载)
包含标准声明和自定义声明的原始JSON内容:
{"sub": "1234567890", "name": "John Doe", "iat": 1516239022}
Base64Url编码后:eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
3. SIGNATURE(签名)
通过头部指定的HS256算法,将编码后的头部、负载与密钥组合加密生成,示例生成逻辑:
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), your-256-bit-secret)
最终Base64Url编码后的签名:d7i1Vw6wFjNpQZ9kLm2Xn3Bv4Cs5Df6Gh7Jk8Lz0
完整JWT令牌:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.d7i1Vw6wFjNpQZ9kLm2Xn3Bv4Cs5Df6Gh7Jk8Lz0
核心问题拆解
- 为什么JWT要分成三个部分,每个部分的作用到底是什么?
- HEADER里的
alg和typ字段分别代表什么,能不能省略? - PAYLOAD里的声明分几种,哪些是必须的,自定义声明需要注意什么?
- SIGNATURE是怎么生成的,它的核心作用是什么?
- Base64Url编码和普通Base64有什么区别,为什么要用它?
问题1:为什么JWT要分成三个部分,每个部分的作用到底是什么?
简单说就是分工明确:
- 头部(HEADER):给令牌打“标签”,告诉接收方该用什么算法验证签名、令牌的类型,让接收方知道如何处理这个令牌。
- 负载(PAYLOAD):存放实际要传递的业务信息,比如用户ID、用户名,相当于令牌的“内容体”。
- 签名(SIGNATURE):令牌的“防伪标识”,用来验证令牌是否被篡改,确保内容的真实性。
问题2:HEADER里的alg和typ字段分别代表什么,能不能省略?
alg:全称Algorithm,指定生成签名的加密算法,比如HS256(对称加密)、RS256(非对称加密),这个字段不能省略——接收方需要用它来验证签名的有效性。typ:全称Type,指定令牌类型,这里固定为JWT,属于可选字段,但加上能让结构更清晰,一般都会保留。
问题3:PAYLOAD里的声明分几种,哪些是必须的,自定义声明需要注意什么?
PAYLOAD里的声明分三类:
- 标准声明:JWT规范定义的可选字段,比如
sub(用户唯一标识)、iat(令牌签发时间)、exp(令牌过期时间),不是强制要求,但使用后能让令牌更规范。 - 公共声明:可自定义的公开字段,建议避免和标准声明重名,最好在IANA的JWT注册表注册,防止字段冲突。
- 私有声明:你和接收方私下约定的自定义字段,比如
role(用户角色),完全自由,但绝对不能存放敏感信息——因为PAYLOAD只是Base64编码,不是加密,任何人解码都能看到内容。
没有强制必须存在的字段,但为了安全,建议加上exp字段设置令牌过期时间,避免令牌永久有效。
问题4:SIGNATURE是怎么生成的,它的核心作用是什么?
生成逻辑很直观:
- 把编码后的HEADER和PAYLOAD用点号拼接,得到字符串:
base64UrlEncode(header) + "." + base64UrlEncode(payload) - 用HEADER指定的算法,搭配密钥(HS256用对称密钥,RS256用私钥)对拼接后的字符串加密,得到的结果就是签名。
核心作用就是防篡改:接收方拿到令牌后,会用同样的方法(对称加密用相同密钥,非对称加密用公钥)重新计算签名,如果和令牌中的签名一致,就说明令牌没有被修改过,是真实有效的。
问题5:Base64Url编码和普通Base64有什么区别,为什么要用它?
普通Base64里的+、/和=字符在URL中会有特殊含义(比如+会被解析成空格),所以JWT使用了Base64Url编码,做了三个小修改:
- 把
+替换成- - 把
/替换成_ - 去掉末尾的填充字符
=
这样编码后的字符串能安全地在URL、HTTP请求头中传递,不会出现解析错误的问题。
问题汇总
- 为什么JWT要分成三个部分,每个部分的作用到底是什么?
- HEADER里的
alg和typ字段分别代表什么,能不能省略? - PAYLOAD里的声明分几种,哪些是必须的,自定义声明需要注意什么?
- SIGNATURE是怎么生成的,它的核心作用是什么?
- Base64Url编码和普通Base64有什么区别,为什么要用它?
内容的提问来源于stack exchange,提问作者Deadpool

