本文为意译,如有误导,请放弃阅读,原文:Introduction to JSON Web Tokens
什么是 JSON Web Token ?
JSON Web Token (JWT) 是一种开放标准 (RFC 7519),它定义了一种紧凑且自包含的方式,用于在各方之间通过 JSON 对象安全地传输信息。该信息可以被验证(verified)和信任(trusted),因为它是经过数字签名的。 JWT 可以使用「密钥(使用 HMAC 算法)」或使用 「RSA 或 ECDSA 的公钥/私钥」对来进行签名。
虽然 我们可以对 JWT 进行加密以在各方之间提供保密,但我们将重点关注签名令牌(signed token)。签名令牌可以验证其中包含的声明的完整性,而加密令牌则对其他方隐藏这些声明。当使用公钥/私钥对对令牌进行签名时,签名还证明只有持有私钥的一方才是对其进行签名的一方。
什么时候该使用 JWT ?
以下就是应该使用 JSON Web Tokens 场景:
-
授权:这是使用 JWT 最常见的场景。用户登录后,每个后续请求都将包含 JWT,允许用户访问该令牌被允许访问的路由、服务和资源。单点登录(Single Sign On)是当今广泛使用 JWT 的一项功能,因为它的开销很小,并且能够轻松跨不同域(domains)使用。
-
信息交换:JSON Web Tokens 是一种在各方之间安全传输信息的好方法。因为 JWT 可以被签名——例如,使用公钥/私钥对——你可以确定信息发送方就是你所想的那个人。此外,由于使用标头和有效负载计算签名,因此您还可以验证内容是否未被篡改。
JWT 的数据结构是怎样的呢?
在其紧凑形式中,JSON Web Tokens 由三个用点 (.) 分隔的部分组成,它们是:
- Header
- Payload
- Signature
因此,JWT 通常看起来是这样的:
xxxxx.yyyyy.zzzzz
Header
header 通常由两部分组成:令牌的类型,即 JWT,以及正在使用的签名算法,例如 HMAC SHA256 或 RSA。举个例子:
{
"alg": "HS256",
"typ": "JWT"
}
然后,就是使用 Base64Url 算法对上面的 json 对象进行编码,编码之后的结果成为 JWT 的第一个部分。
Payload
令牌的第二部分是 payload,其中包含声明(claim)。声明是关于实体(通常是用户)和附加数据的声明。共有三种类型的索赔:注册、公共和私有声明(here are three types of claims: registered, public, and private claims.)。
- 注册声明:这些是一组预定义的声明,这些声明不是强制性的,而是推荐的,以提供一组有用的、可互操作的声明。其中一些是:iss(发行者)、exp(到期时间)、sub(主题)、aud(受众)等。
note: 声明名称只有三个字符,因为 JWT 志在紧凑的。
-
公共声明:这些可以由使用 JWT 的人随意定义。但是为了避免冲突,它们应该在 IANA JSON Web Token Registry 中定义或定义为包含抗冲突命名空间的 URI。
-
私人声明:这些自定义声明是为了在同意使用它们的各方之间共享信息而创建的,这些声明既不是注册声明也不是公开声明。
举个例子:
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
然后,就是使用 Base64Url 算法对上面的 json 对象进行编码,编码之后的结果成为 JWT 的第二个部分。
请注意,对于已签名的令牌,此信息虽然受到「防篡改」保护,但任何人都可以「读取」。除非加密,否则不要将机密信息放在 JWT 的负载或标头元素中。
Signature
要创建签名部分,您必须获取编码的标头、编码的有效载荷、密钥、标头中指定的算法,并对其进行签名。 例如,如果要使用 HMAC SHA256 算法,则签名将通过以下方式生成:
signature = HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
签名用于验证消息在此过程中有没有被串改,并且在使用私钥签名的令牌的情况下,它还可以验证 JWT 的发送者是它所说的那个人。
组合到一块
输出是三个由点分隔的 Base64-URL 字符串,可以在 HTML 和 HTTP 环境中轻松传递,同时与基于 XML 的标准(如 SAML)相比更加紧凑。
下面显示了一个 JWT,它对前面所提到的标头和有效负载进行了编码,并使用密钥进行了签名。
如果您想玩转 JWT 并将这些概念付诸实践,您可以使用 jwt.io Debugger 来解码、验证和生成 JWT。
JWT 的工作原理
在身份验证中,当用户使用其凭据(一般是指用户名 + 密码)成功登录时,将返回 JSON Web Token。由于令牌是凭证,因此必须非常小心以防止出现安全问题。通常,您不应将令牌保留的时间超过所需的时间。
由于缺乏安全性,您也不应该将敏感的会话数据存储在浏览器存储中。
每当用户想要访问受保护的路由或资源时,用户代理应该发送 JWT,通常在使用 Bearer 模式的 Authorization 标头中。标题的内容应如下所示:
Authorization: Bearer <token>
在某些情况下,这可以是无状态授权机制。服务器的受保护路由将检查 Authorization 标头中的有效 JWT,如果存在,将允许用户访问受保护资源。如果 JWT 包含必要的数据,则可能会减少为某些操作查询数据库的需要,尽管情况并非总是如此。
如果令牌在 Authorization 标头中发送,则跨源资源共享 (CORS) 不会成为问题,因为它不使用 cookie。
下图显示了如何获取 JWT 并将其用于访问 API 或资源:
-
应用程序或客户端向授权服务器请求授权。这是通过不同的授权流程之一执行的。例如,典型的 OpenID Connect 兼容 Web 应用程序将使用授权代码流通过
/oauth/authorizeend point。 -
当授权被同意之后,授权服务器向应用程序返回一个访问令牌(access token)。
-
应用程序使用访问令牌来访问受保护的资源(如 API)。
请注意,使用签名令牌,令牌中包含的所有信息都会暴露给用户或其他方,即使他们无法更改它。这意味着您不应将秘密信息放入令牌中。
为什么我们要使用 JWT ?
让我们来谈谈与简单网络令牌 (Simple Web Tokens - SWT) 和安全断言标记语言令牌 (Security Assertion Markup Language Tokens - SAML) 相比,JWT 所拥有的优势。
由于 JSON 没有 XML 冗长,因此在编码时它的体积也更小,这使得 JWT 比 SAML 更紧凑。这使得 JWT 成为在 HTML 和 HTTP 环境中传递的不错选择。
在安全方面,SWT 只能由使用 HMAC 算法的共享密钥来进行对称签名。但是,JWT 和 SAML 令牌可以使用 X.509 证书形式的公钥/私钥对进行签名。与对 JSON 签名的简单性相比,在不引入模糊安全漏洞的情况下使用 XML 数字签名来对 XML 签名是非常困难的。
JSON 解析器在大多数编程语言中都很常见,因为它们直接映射到对象。相反,XML 没有语言原生级别的文档到对象映射实现。这使得使用 JWT 比使用 SAML 更容易。
关于使用,JWT 用于互联网规模。这突出了客户端在多个平台(尤其是移动平台)上处理 JSON Web 令牌的简便性。
编码后的 JWT 与 编码后的 SAML 的字符串长度对照图:
如果您想阅读有关 JWT 的更多信息,甚至开始使用它们在您自己的应用程序中执行身份验证,请浏览到 Auth0 上的 JSON Web Token landing page