JWT 是什么?token 里存了什么,为什么不能放隐私
一、三段结构:点号分开的三块
一个典型的 JWT 长这样(用点号分成三段):
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMDI0IiwibmFtZSI6IuW8oOS4iSIsInJvbGUiOiJ1c2VyIiwiaWF0IjoxNzg5MDAwMDAwLCJleHAiOjE3ODkwMDM2MDB9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
| 段 | 内容 | 编码后示例 |
|---|---|---|
| 第一段 | 头部 header | eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 |
| 第二段 | 载荷 payload | eyJzdWIiOiIxMDI0IiwibmFtZSI6IuW8oOS4iSIs… |
| 第三段 | 签名 signature | SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c |
解码前两段(Base64URL 解码)就能看到明文内容:
{"alg":"HS256","typ":"JWT"}
{"sub":"1024","name":"张三","role":"user","iat":1789000000,"exp":1789003600}
这是理解 JWT 的关键:前两段不是加密,只是「换个写法」。 任何人拿到 token 都能解出里面的字段——包括中间人、日志系统、以及任何一个在线解码工具。
二、所以:token 里不要放敏感信息
| 可以放 | 不要放 |
|---|---|
| 用户 ID、角色、租户标识 | 手机号、身份证号、银行卡号 |
| 权限范围(scope)、令牌版本 | 密码、密码哈希、密钥 |
| 过期时间、签发时间 | 任何「泄露就有损失」的个人信息 |
JWT 只保证完整性(内容没被改过,前提是验签正确),不保证保密性。要保密必须加密(JWE 或服务端 session)。
三、三个时间字段:iat / nbf / exp
三者都是 epoch 秒(10 位整数,不是毫秒):
| 字段 | 含义 | 说明 |
|---|---|---|
iat | 签发时间 | 用于审计、判断令牌新鲜度 |
nbf | 生效时间 | 早于它服务端应拒绝(可选) |
exp | 过期时间 | 到点失效,是最常用的字段 |
上面那个例子里 exp 是 1789003600,解码工具会直接标出「已过期」——因为它已经早于当前时间。
两个细节:
- exp 是令牌自己声明的,服务端如果压根不校验,过期的 token 照样能用。看到「已过期」应按失效处理,但不能假定「没过期就一定安全」;
- 时钟偏差会造成误判:客户端与服务端时间差几分钟,就可能让刚签发的 token 被判成未生效。工程上一般留 30~60 秒容差。
四、为什么不验签
验签需要密钥或公钥:HMAC 算法要对密钥、RSA/ECDSA 要对公钥。把这些放到网页前端等于直接公开,任何人都能自己签发任意内容的 token。所以在线工具只解结构、不冒充校验:
- 能判断:格式是否合法、三段能否解码、时间字段状态、
alg是什么; - 不能判断:令牌是否真实、是否被篡改、签发方是谁。
验签永远在服务端做,而且要显式指定允许的算法列表——不校验 alg 就可能被「算法混淆」攻击(把 HS256 换成 none 或反之)。
五、三个常见误解
- 「Base64 是加密」:不是。Base64 与 Base58、Base32 一样只是编码,用来换一种字符写法,没有密钥、一键还原;
- 「签名能防别人看内容」:不能。签名只防篡改,内容依然是明文;
- 「token 越长越安全」:长度只和载荷大小有关,安全性取决于签名算法强度、密钥长度与服务端校验是否到位。
还有一个常见发现:如果第三段是空的(token 以点号结尾),说明这是个 alg: none 的未签名令牌——生产环境出现它意味着鉴权形同虚设。
六、直接算
→ JWT 解析器:本地解码三段结构,标出 exp/iat/nbf 状态与 alg
→ Base58 编解码:另一种「换写法不加密」的编码,好抄写但不保密
→ 编码转换:Base64、URL 编码、Unicode 转义的互转
本站文章为个人使用经验的原创整理,除已注明来源的引用外均为本人撰写。文中涉及的软件名称、商标、截图等归各自权利人所有,引用仅用于说明与交流。如认为本站内容侵犯了你的合法权益,请通过「关于本站」页面的联系方式告知,核实后将及时更正或删除。