JWT结构详解:Header、Payload、Signature各自的作用

JWT看起来像一串杂乱无章的字符,但只要把它拆成实际的三段,结构其实非常简单。

用点号连接的三段内容

JWT的完整形式是 header.payload.signature——三段用英文句点分隔的Base64url编码内容。header描述令牌类型和签名算法,payload携带实际的声明(claims)信息,signature用来验证前两部分没有被篡改。

它是编码,不是加密

Base64url编码是任何人都能在不需要密钥的情况下还原的可逆处理,它存在的目的是让二进制安全的数据便于放进URL和JSON里,而不是为了隐藏内容。任何标准JWT的header和payload,粘贴进一个基础的解码工具就能被任何人读出来。

payload里绝不应该放机密信息

由于任何持有该令牌的人都能直接读出payload的内容,像密码或个人隐私细节这类敏感数据,绝不应该放进标准JWT的声明里——应该把payload当作可见信息,而不是保密信息来对待。

签名证明的是"完整性",不是"保密性"

签名让服务器能够验证这个令牌确实由可信来源签发、且签发后没有被更改过,但它并不能对截获该令牌的第三方隐藏payload的内容。验证签名需要用到签名密钥,但解码本身并不需要。

几个反复出现的标准声明字段

exp(过期时间)和iat(签发时间)是Unix时间戳,用来控制令牌的有效期;sub(主体)通常用来标识用户;iss(签发者)标识是谁创建了这个令牌。应用程序可以在这些之外再添加自定义的声明字段。

为什么JWT在身份验证场景中变得流行

有了JWT,服务器不需要在每次请求时都去数据库查询会话信息,因为令牌本身就携带了可验证的声明和过期时间。这种无状态特性让JWT很适合分布式系统和API场景,代价是相比传统的服务端会话,它更难被立即撤销。

解码令牌和验证令牌是完全不同的两回事

任何人都可以在不需要任何密钥的情况下解码出JWT的header和payload,因为这只是编码而已。要验证一个令牌是否真实、是否未被修改,则需要用到签发时使用的签名密钥或公钥——一个只解码而未经验证的令牌,不应该被当作任何证明来信任。

常见问题

不需要密码就能读出JWT的payload内容,这算不算安全漏洞?

不算,这是设计上就预期的行为。JWT本来就是设计成可被读取的,它提供的安全保证是通过签名实现的完整性(能被察觉篡改),而不是payload内容的保密性。

可以修改JWT的payload内容之后继续使用吗?

可以修改解码后的文本内容,但如果没有正确的签名密钥重新编码,得到的签名会变得无效,因此正确执行验证的服务器会拒绝这个被篡改过的令牌,而不会接受修改后的声明。