分享一篇详尽的关于如何在 JavaScript 中实现刷新令牌的指南

151ceb72c013f4cc78415edc96b38ea1.jpeg

介绍

刷新令牌允许用户无需重新进行身份验证即可获取新的访问令牌,从而确保更加无缝的身份验证体验。这是通过使用长期刷新令牌来获取新的访问令牌来完成的,即使原始访问令牌已过期也是如此。

通常,当用户登录时,服务器会生成一对令牌:访问令牌和刷新令牌。访问令牌的生命周期很短,用于对用户进行身份验证并授予他们对受保护资源的访问权限。刷新令牌具有较长的生命周期,用于在原始访问令牌过期后获取新的访问令牌。

当访问令牌过期时,客户端将刷新令牌发送到服务器,然后服务器验证刷新令牌并生成新的访问令牌。此过程在后台发生,用户无需重新输入凭据。用户可以不间断地继续访问受保护的资源。这样,用户就不必重复登录,从而实现无缝的身份验证体验。

此外,刷新令牌还为服务器提供了一种撤销用户访问权限的方法,而无需用户重新进行身份验证。通过使刷新令牌无效,服务器可以阻止用户获取新的访问令牌,从而有效地将他们从系统中注销。

总之,刷新令牌是一个强大的工具,可在您的应用程序中维持无缝且安全的身份验证体验。它们允许用户继续访问受保护的资源而无需重新进行身份验证,同时还为服务器提供了一种在必要时撤销访问的方法。

OAuth 2.0 和 JWT

OAuth 2.0 是一种开放的授权标准,使应用程序能够通过授权服务器访问资源服务器(通常是 API)上的资源所有者(通常是用户)的资源。JWT(JSON Web 令牌)是一种紧凑、URL 安全的方式,用于表示要在两方之间传输的声明。

在 OAuth 2.0 中,JWT 可以用作访问令牌和/或刷新令牌。访问令牌用于访问受保护的资源,例如 API,而刷新令牌用于在当前访问令牌过期时获取新的访问令牌。

当 JWT 用作访问令牌时,它通常使用用户的声明和令牌的过期时间进行编码。然后,资源服务器可以解码令牌以验证用户的身份并授权访问受保护的资源。

当 JWT 用作刷新令牌时,它通常使用指示当前访问令牌的过期时间的声明进行编码。当当前访问令牌过期时,客户端可以使用刷新令牌来获取新的访问令牌。

总之,OAuth 2.0 提供了一个用于保护资源访问的框架,而 JWT 提供了一种紧凑且安全的方式来编码和在各方之间传输声明。OAuth 2.0 和 JWT 可以一起用于为 Web 和移动应用程序创建安全高效的授权系统。

JWT 令牌的结构

这是遵循 JWT 格式的解码访问令牌的内容:

{
  "iss": "https://YOUR_DOMAIN/",
  "sub": "auth0|123456",
  "aud": [
    "my-api-identifier",
    "https://YOUR_DOMAIN/userinfo"
  ],
  "azp": "YOUR_CLIENT_ID",
  "exp": 1489179954,
  "iat": 1489143954,
  "scope": "openid profile email address phone read:appointments"
}

在其紧凑形式中,JSON Web 令牌由用点 (.) 分隔的三个部分组成,它们是:

  • 标头(Header)

  • 有效载荷(Payload)

  • 签名(Signature)

因此,JWT 通常如下所示。

xxxxx.yyyyy.zzzzz

让我们分解不同的部分。

标头(Header)

标头通常由两部分组成:令牌的类型(JWT)和所使用的签名算法(例如 HMAC SHA256 或 RSA)。

例如:

{
 "alg": "HS256",
 "typ": "JWT"
}

然后,对该 JSON 进行 Base64Url 编码以形成 JWT 的第一部分。

有效载荷(Payload)

令牌的第二部分是有效负载,其中包含声明。声明是关于实体(通常是用户)和附加数据的声明。索赔分为三种类型:注册索赔、公共索赔和私人索赔。

  • 注册声明:这些是一组预定义的声明,不是强制性的,而是推荐的,以提供一组有用的、可互操作的声明。其中一些是:iss(发行者)、exp(到期时间)、sub(主题)、aud(受众)等。

  • 请注意,声明名称只有三个字符长,因为 JWT 旨在紧凑。

  • 公共声明:这些可以由使用 JWT 的人随意定义。但为了避免冲突,它们应该在 IANA JSON Web 令牌注册表中定义,或者定义为包含防冲突命名空间的 URI。

  • 私人声明:这些是为在同意使用它们的各方之间共享信息而创建的自定义声明,既不是注册声明也不是公开声明。

有效负载示例可以是:

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}

然后对有效负载进行 Base64Url 编码以形成 JSON Web 令牌的第二部分。

默认支持七个注册声明名称:

iss": (Issuer)声明,"iss"(issuer)声明标识发布JWT的主体。

"exp": (Expiration Time)声明,"exp"(expiration time)声明标识JWT不得在其到期时间之后或之时接受处理。

"sub": (Subject)声明,"sub"(subject)声明标识JWT的主体。

"aud": (Audience)声明,"aud"(audience)声明标识JWT的接收者。

"nbf": (Not Before)声明,"nbf"(not before)声明标识JWT不能在此时间之前接受处理。

"iat": (Issued At)声明,"iat"(issued at)声明标识JWT的发行时间。

"jti": (JWT ID)声明,"jti"(JWT ID)声明为JWT提供唯一标识符。

签名(Signature)

要创建签名部分,您必须获取编码的标头、编码的有效负载、秘密、标头中指定的算法,然后对其进行签名。

例如,如果要使用HMAC SHA256算法,则将通过以下方式创建签名:

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

签名用于验证消息在传输过程中没有发生更改,并且在使用私钥签名的令牌的情况下,它还可以验证 JWT 的发送者是否是其所说的人。

将所有内容放在一起

输出是三个由点分隔的 Base64-URL 字符串,可以在 HTML 和 HTTP 环境中轻松传递,同时与基于 XML 的标准(例如 SAML)相比更加紧凑。这是使用 jwt.io 解码编码令牌的示例。

5447ba992ca5c36433f18bb699ed3ea7.jpeg

实施刷新令牌

请务必记住,OAuth 2.0 规范定义了访问令牌和刷新令牌。因此,如果我们根据其他身份协议或框架(例如 SAML)讨论授权策略,我们将不会有访问令牌或刷新令牌的概念。

以下是应用程序如何在 Node.js 应用程序中使用 JWT 刷新令牌的示例:

  • 用户登录到应用程序并将其凭据发送到身份验证服务器。

  • 身份验证服务器验证凭据,生成 JWT 访问令牌和 JWT 刷新令牌。访问令牌包含用户的声明(例如,用户 ID、角色等),刷新令牌包含指示访问令牌过期时间的声明。

  • 身份验证服务器将访问令牌和刷新令牌发送给客户端。

  • 客户端将令牌存储在本地存储中或作为仅 HTTP 的安全 cookie。

  • 客户端在每个访问受保护资源的请求中发送访问令牌。

  • 当访问令牌过期时,客户端将刷新令牌发送到认证服务器以获取新的访问令牌。

  • 身份验证服务器验证刷新令牌并检查过期时间声明。如果刷新令牌有效且未过期,则身份验证服务器会颁发具有新过期时间的新访问令牌。

  • 身份验证服务器将新的访问令牌发送给客户端。

  • 客户端存储新的访问令牌并继续使用它来访问受保护的资源。

本示例使用 JWT 作为独立的刷新令牌,它可以存储在客户端,可用于跨多个域对用户进行身份验证和授权。

以下代码示例展示了如何在 Python 脚本中使用刷新令牌来确保用户的无缝体验:

4f8ce1ee009c50741fee00b2a497d99b.jpeg

此示例使用 jwt 库来解码 JWT 访问令牌,并使用 requests 库发出 HTTP 请求。该脚本首先向令牌端点发出初始请求以获取访问令牌和刷新令牌。然后,对访问令牌进行解码以获取过期时间,并在向受保护端点发出请求之前检查该过期时间。如果访问令牌已过期,脚本将使用刷新令牌来获取新的访问令牌,然后重试原始请求。

请注意,这是一个简单的示例,在现实场景中,您应该处理错误,并且应该使用为您处理令牌流(例如 pyJWT)的库或框架,并且您不应该对凭证、端点和代码中的secret_key。您还应该使用安全的方式来传输令牌并保证secret_key的安全

使刷新令牌无效

如果刷新令牌遭到泄露,您可以撤销它们。

可以在服务器端通过将令牌添加到黑名单或在数据库中将其标记为已撤销来使刷新令牌失效。以下是如何使用 Node.js 和 MongoDB 使刷新令牌失效的示例:

f62b54de653f165422d63681b46db0f4.jpeg

在此示例中,我们使用 Mongoose 库与 MongoDB 数据库进行交互,并且定义了一个 RefreshToken 模型,该模型映射到数据库中的刷新令牌集合。invalidateRefreshToken函数以token为参数,在数据库中查找对应的刷新token。如果找到令牌,则会将该令牌标记为已撤销并将其保存在数据库中。如果未找到令牌,则返回错误。

这只是一个示例,您可以根据您的堆栈和架构进行调整。需要注意的是,这个例子只是一个服务器端实现,您还需要相应地处理客户端。

还需要注意的是,此示例不适合生产,因为它仅将令牌标记为已撤销,并且不处理令牌黑名单。在生产环境中,建议使用Redis等分布式机制来处理黑名单。

代码示例:客户端使刷新令牌失效

在客户端,可以通过从客户端存储中删除令牌并确保客户端不会再次使用该令牌来使刷新令牌失效。以下是如何使用 JavaScript 使刷新令牌失效的示例:

c9175cd4df2d0401afc91925a6111ebc.jpeg

在此示例中,我们使用 localStorage 对象来存储和检索刷新令牌。调用 invalidateRefreshToken 函数时,它会从客户端存储中检索刷新令牌并将其删除。然后它向服务器发出获取请求以使令牌无效。服务器应该有一个监听此请求的路由,如前面的示例所示。

需要注意的是,此示例使用 localStorage 来存储令牌。您可以使用其他存储方法,例如 sessionStorage 或 cookie。另外,这个示例是为了演示目的而以简单的方式完成的,在生产环境中建议使用 axios 等库来发出 HTTP 请求。

还需要注意的是,这个示例只是一个客户端实现。

总结

总之,实施刷新令牌是在 Web 应用程序中提供无缝、安全的用户体验的关键一步。通过使用刷新令牌,您可以确保用户保持登录状态,同时最大限度地降低安全风险。本文提供的指南(包括如何使用 JavaScript 实现刷新令牌的示例)应该为您重振身份验证过程提供一个良好的起点。

值得注意的是,实施刷新令牌并不是一种万能的解决方案,了解所涉及的权衡非常重要。例如,使用刷新令牌会增加应用程序的复杂性,如果处理不当,还会增加令牌泄露的风险。因此,彻底测试您的实施并留意任何潜在的安全漏洞非常重要。

最后,建议使用为您处理令牌流的库或框架,这可以使实现刷新令牌的过程变得更加容易和安全。使用安全的方式来传输令牌并保证 Secret_key 的安全也很重要。

总的来说,在身份验证过程中加入刷新令牌可以极大地改善用户体验并提高 Web 应用程序的安全性。通过本指南,您现在应该具备在 JavaScript 应用程序中实现刷新令牌所需的知识和工具。

由于文章内容篇幅有限,今天的内容就分享到这里,文章结尾,我想提醒您,文章的创作不易,如果您喜欢我的分享,请别忘了点赞和转发,让更多有需要的人看到。同时,如果您想获取更多前端技术的知识,欢迎关注我,您的支持将是我分享最大的动力。我会持续输出更多内容,敬请期待。

猜你喜欢

转载自blog.csdn.net/Ed7zgeE9X/article/details/132013805