简单 5 步理解 JWT (JSON Web Tokens)

时间:2023-01-18 05:39:50

翻译自:5 Easy Steps to Understanding JSON Web Tokens (JWT)

在本文中,将解释 JWT (JSON Web Tokens) 的基本原理,以及它们为什么会被使用。JWT 是确保应用程序中的信任和安全性的重要部分。JWT 允许声明 (例如用户数据)以安全的方式表示。

要解释 JWT 的工作原理,让我们从一个抽象定义开始。

一个 JWT ( JSON Web Token)是一个 JSON 对象,它在 RFC 7519 中定义为一种安全的方式来表示两方之间的一组信息。Token 有 头部、有效载荷和签名组成。

简而言之,JWT 只是一个具有如下格式的字符串:

header.payload.signature

需要指出:一个双引号的字符串实际上被认为是一个有效的 JSON 对象。

为了说明 JWT 实际使用的方式和原因,我们将使用一个简单的 3 实体示例(参见下图)。本例中的实体是用户、应用服务器和身份验证服务器。身份验证服务器将向用户提供 JWT。通过 JWT,用户可以安全地与应用程序通信。

简单 5 步理解 JWT (JSON Web Tokens)简单 5 步理解 JWT (JSON Web Tokens)应用程序如何使用 JWT 来验证用户的真实性。

本例中,用户使用身份验证服务器的登录系统(如用户名和密码、Facebook 登录、谷歌登录等)进入身份验证服务器。然后,身份验证服务器创建 JWT 并将其发送给用户。当用户对应用程序进行 API 调用时,用户将通过 JWT 和API 调用。在此设置中,将配置应用程序服务器,以验证传入的 JWT 是由身份验证服务器创建的(稍后将更详细地解释验证过程)。因此,当用户使用附加的 JWT 进行 API 调用时,应用程序可以使用 JWT 来验证 API 调用是否来自经过身份验证的用户。

接下来,将会更深入地进行研究 JWT,以及它是如何构造和验证的。

第一步:生成头部

JWT 的头组件包含关于如何计算 JWT 签名的信息。

头部是一个具有如下格式的 JSON 对象:

{

在这个 JSON 中,“typ” 键的值指定该对象是 一个 JWT,而 “alg” 键的值指定用于创建 JWT 签名组件的哈希算法。在我们的例子中,我们使用了 hmc - sha256 算法,一个使用密钥的散列算法来计算签名(在步骤 3 中更详细地讨论)。

第二步:生成 PAYLOAD (有效载荷)

JWT 的有效载荷组件是存储在 JWT 中的数据(此数据也称为 JWT 的“声明”)。在我们的示例中,身份验证服务器使用存储在其中的用户信息创建一个 JWT,特别是用户 ID。

{

有效载荷内的数据称为 Token 的“声明”。

在我们的示例中,我们只将一个声明放入有效负载中。你可以根据自己的喜好插入很多声明。JWT 有效载荷有几种不同的标准要求,如“iss”、“sub” 和 “exp” 的过期时间。这些字段在创建 JWT 时非常有用,但是它们是可选的。有关 JWT 标准字段的更详细列表,请参阅 JWT wikipedia

请记住,数据的大小将影响 JWT 的总体大小,这通常不是问题,但是过大的 JWT 可能会对性能造成负面影响,并导致延迟。

第三步:生成签名

使用如下的伪代码计算签名:

// 签名算法
data = base64urlEncode( header ) + “.” + base64urlEncode( payload )
signature = Hash( data, secret );

本算法的功能是分别把第一步和第二部生成的头部和有效载荷进行 base64url 编码。接着使用 (.)把编码后的两者连接起来。在我们的伪代码中,连接后的字符串赋值给 data 变量。JWT 签名是由数据字符串使用 JWT 头中指定的散列算法与密钥进行散列。

在我们的示例中,头部和有效载荷的 base64url 编码结果为:

// 头部
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
// 有效载荷
eyJ1c2VySWQiOiJiMDhmODZhZi0zNWRhLTQ4ZjItOGZhYi1jZWYzOTA0NjYwYmQifQ

接着,使用头部和有效载荷连接后的字符串上应用指定的签名算法 (HS256),将 ‘secret’ 字符串设置为密匙。我们将生成如下的 JWT 签名:

// 签名
-xN_h82PHVTCMA9vdoHrcZxH-x5mb11y1537t3rGzcM

第四步:将 JWT 的三部分连接成一个字符串

当我们生成 JWT 的三部分,我们就可以生成 JWT。记住 JWT 的结构: header.payload.signature,我们使用 (.) 将三部分连接起来。我们使用了头部和有效载荷的 base64url 编码,以及我们在步骤 3 中生成的签名。

// JWT Token
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOiJiMDhmODZhZi0zNWRhLTQ4ZjItOGZhYi1jZWYzOTA0NjYwYmQifQ.-xN_h82PHVTCMA9vdoHrcZxH-x5mb11y1537t3rGzcM

你也可以通过 jwt.io 网站来生成 JWT。

回到我们的例子,此时认证服务器将生成的 JWT 发送给用户。

JWT 如何保护我们的数据?

重要的是要理解,使用 JWT 的目的不是以任何方式隐藏数据。使用 JWT 的原因是为了证明发送的数据实际上是由一个真实的源创建的。

正如前面的步骤所示,JWT中的数据是经过编码和签名的,而不是加密的。编码数据的目的是改变数据的结构。签名数据允许数据接收者验证数据来源的真实性。因此,编码和签名数据不能保证数据的安全。另一方面,加密的主要目的是保护数据并防止未经授权的访问。关于编码和加密之间的区别的更详细的解释,以及关于哈希如何工作的更多信息,请参阅 本文

由于 JWT 只有签名和编码,而且 JWT 没有加密,因此 JWT 不能保证敏感数据的安全性。

第五步:验证 JWT

在我们简单的 3 个实体示例中,我们使用的是由 HS256 算法签名的 JWT,其中只有身份验证服务器和应用服务器知道密匙。当应用程序设置其身份验证过程时,应用服务器从身份验证服务器接收密钥。由于应用程序知道密匙,当用户向应用程序发出 JWT 附加的 API 调用时,应用程序可以在 JWT 上执行与步骤 3 相同的签名算法。然后,应用程序可以验证从它自己的散列操作中获得的签名是否与 JWT 本身的签名相匹配(即它匹配由身份验证服务器创建的 JWT 签名)。如果签名匹配,则意味着 JWT 是有效的,这表明 API 调用来自一个真实的源。否则,如果签名不匹配,则意味着接收的 JWT 无效,这可能是对应用程序的潜在攻击的指示器。因此,通过验证 JWT,应用程序在自己和用户之间添加了一层信任。

总结

我们讨论了 JWT 是什么,它们是如何创建和验证的,以及如何使用它们来确保应用程序和它的用户之间的信任。这是了解 JWT 的基本原理以及它们为什么有用的起点。在确保应用程序的信任和安全性方面,JWT 只是一个难题。