什么是 JSON Web Token (JWT)?
5分钟内清晰地理解 JSON Web Token (JWT)基础知识。
JSON Web Token (JWT) 在现代网络应用和开放标准如 OpenID Connect 中得到了广泛使用,支持身份验证和授权。虽然官方的 RFC 7519 提供了必要的参考信息,但对初学者来说理解起来会有所挑战。在本文中,我们将专注于 JWT 的核心概念,并结合案例,用通俗易懂的语言来阐释它们。
我们为什么需要 JWT?
如今,使用 JSON 在两个实体之间交换数据已经非常常见。我们可以考虑一个代表用户的 JSON 对象:
sub
是 "subject" 的缩写,它在 OpenID Connect 中作为标准声明来代表用户标识符(用户 ID)。
我们如何保证这个 JSON 对象的完整性呢?也就是说,如何确保数据在传输过程中不被篡改?一个常见的解决方案是使用数字签名。例如,我们可以使用公钥加密:服务器使用私钥签署 JSON 对象,客户端可以使用服务器的公钥来验证签名。
简单来说,JWT 提供了一种标准化的方式来表示 JSON 对象及其签名。
JWT 的格式
由于有许多方式可以创建数字签名,因此有必要指定用于 JWT 签名的算法。这可以通过构造 JSON 对象来完成:
alg
是 "algorithm" 的缩写,typ
是 "type" 的缩写。
通常,typ
被设置为大写的 JWT
。对于我们的例子,alg
是 HS256
,它代表了 HMAC-SHA256(我们稍后会解释它),并且表示我们正在使用这个算法来创建签名。
到目前为止,我们已经拥有了 JWT 中所有的成分:
- Header JSON:算法和类型
- Payload JSON:实际数据
- Signature:包含 header 和 payload 的签名
然而,空格和换行等某些字符不对网络传输友好。因此,header 和 payload 需要进行Base64URL-编码。典型的 JWT 看起来是这样的:
.
起到了分隔的作用。
让我们把所有的东西放在一起,创建一个 JWT:
Header
JSON:{"alg":"HS256","typ":"JWT"}
Base64URL 编码:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
Payload
JSON:{"sub":"foo","name":"John Doe"}
Base64URL编码:eyJzdWIiOiJmb28iLCJuYW1lIjoiSm9obiBEb2UifQ
Signature
在 HMAC-SHA256 中,签名是由一个秘密创建的:
例如,当 secret 为 some-great-secret
时,签名变为:XM-XSs2Lmp76IcTQ7tVdFcZzN4W_WcoKMNANp925Q9g
。
JWT
最后的 JWT 是:
这个有效的 JWT 可以由任何拥有 secret 的方验证。
选择签名算法
如前所述,有许多算法可以创建数字签名。我们以 HS256
为例,但它可能不够强大,因为 secret 必须在双方(例如客户端和服务器)之间共享。
在实际场景中,客户端可能包括无法保证 secret 安全的公共应用,例如 React 应用。因此,首选的做法是使用公钥加密(即非对称加密)来签署 JWT。让我们从最流行的算法:RSA 开始。
RSA
RSA 是一种非对称算法,它使用一对密钥:公钥和私钥。公钥用于验证签名,私钥用于签名。
RSA 的 header JSON 如下所示:
RS256
代表 RSA-SHA256,这意味着签名是由 RSA 算法和 SHA256 哈希函数生成的。你也可以使用RS384
和RS512
来分别用 SHA384 和 SHA512 哈希函数创建签名。
签名是用私钥创建的:
再次展示,我们可以将这些部分组合起来创建一个 JWT,最后的 JWT 看起来是这样的:
现在,客户端可以在不知道私钥的情况下验证签名。
ECDSA
虽然 RSA 得到了广泛的使用,但它的签名体积较大,有时会超过 header 和 payload 的总大小。椭圆曲线数字签名算法(ECDSA)是另一种非对称算法,它可以创建更小的签名,并且更加高效。
为了为 ECDSA 生成私钥,我们需要选择一条曲线。这超出了本文的范围,但你可以在这里找到更多相关信息。
ECDSA 的 header JSON 如下所示:
ES256
代表 ECDSA-SHA256,这意味着签名是由 ECDSA 算法和 SHA256 哈希函数生成的。你也可以使用ES384
和ES512
来分别用 SHA384 和 SHA512 哈希函数创建签名。
签名是用私钥创建的:
最后的 JWT 保持与 RSA 相同的结构,但其签名明显更短:
验证 JWT
验证 JWT 简单到可以看作是创建 JWT 的反向过程:
- 使用
.
分隔符将 JWT 分为三个部分(header,payload,签名)。 - 使用 Base64URL 对 header 和 payload 进行解码。
- 使用 header 中指定的算法和公钥(适用于非对称算法)来验证签名。
有许多库可以帮助进行 JWT 验证,例如针对 Node.js 和 web 浏览器的 jose 。
结论
在本文中,我们简要解释了 JWT 的核心概念,并概述了如何创建和验证它。还有许多细节我们没有探讨,我们将在未来的文章中介绍它们。
Logto 利用像 JWT 和 OpenID Connect 这样的开放标准来保护你的应用程序和 API,并为每个开发者简化工作流。如果你有兴趣,你可以免费试用(无需信用卡)。