这篇文章翻译自 JWT 官网: https://jwt.io/introduction/
JWT(JSON 网络令牌)是一个开源的标准(RFC 7519)。这个标准定义了一个简洁并且自包含的方法,以便在系统各个部分之间安全地传送JSON对象格式的信息。因为通过JWT传递的信息是被数字签名过的,所以是可以被证实和信任的。我们可以使用单一密钥来加密 JWT 的签名,比如 HMAC 算法;也可以使用 RSA 的公私密钥对来加密 JWT 的签名。
让我们更深入地来解释一下这个定义中的一些概念:
简洁:因为 JWT 体积比较小,我们可以通过 URL,POST参数或者 HTTP 报头来发送 JWT。另外,较小的体积意味着发送速度比较快。
自包含:装载的数据包含和用户相关的所有需要的信息,避免了额外再查询一次数据库的需求。
这里有一些适合使用 JWT 的场景:
身份验证:这是 JWT 最常用的场景。一旦用户登录进系统,每一个后继的请求都会包含 JWT。系统允许用户访问那些令牌有权限的路由,服务和资源。单点登录是一个在当代广泛使用 JWT 的特性,这是因为下面两点原因:其一是 JWT 的系统开销很小;其二是 JWT 能够轻而易举地应用到跨域的场景中。
信息交换:JWT 是一种在系统各个部分之间安全地传送信息的方法。这是因为只要 JWT 可以被数字签名,比如使用公私密钥对,你就可以信任 JWT 中包含的发送者身份信息。除此之外,当签名在计算的时候,使用了 JWT 的头部部分和负载部分,你也可以验证信息的内容有没有被篡改过。
JWT 由三部分组成,三个部分之间用英语句号(.)分割。这三个部分分别是:
因此,一个典型的 JWT 令牌看起来像下面这个样子:
xxxx.yyyy.zzzz
让我们接下来分析不同的部分。
一个典型的头部由两部分组成。第一部分是令牌的类型,一般是JWT。第二部分是用到的哈希算法,比如像 HMAC、SHA256 或 RSA。比如:
{
"alg": "HS256",
"typ": "JWT"
}
然后,这个 JSON 会以 Base64Url 进行编码,并作为 JWT 的第一部分。
令牌的第二部分是负载,负载包含声明。声明是与一个实体(通常是用户)和额外的元信息相关的语句。有三种类型的声明:保留型声明、公开型声明和私有型声明。
私有型声明:有一套非强制但推荐的预定义声明,来为使用者提供一系列有用的,可以互操作的声明。它们中的一些是:iss(发行者)、exp(过期时间)、sub(主题)、aud(观众)和其它的。
注意声明的名称只有三个字符的长度,这是由于 JWT 被普遍认为应当是简洁的。
公开型声明:这些声明可以被 JWT 的使用者随意地定义。但是为了避免冲突,它们应该在 “JSON 网络令牌网络分配号码授权注册中心“(IANA JSON Web Token Registry)进行定义。或者定义的时候像 URI 一样包含一个防止冲突的命名空间。
私有型声明:这种类型是自定义的声明。这种声明被创建出来,用来在系统中各个允许使用私有型声明的部分共享信息。
一个负载的例子可以是这样的:
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
然后负载就按照 Base64Url 编码,并组成 JWT 的第二部分。
为了生成签名部分,你不得不取得编码后的头部、编码后的负载、一个密钥和头部中指定的算法,然后生成签名。
举个例子,如果你想使用 HMAC SHA256 算法,签名将会按照下面的方式被创建出来:
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
签名用来证实 JWT 的发送者就是 JWT 中所写的那样。并且签名还用来确保信息在传送途中没有被篡改。
输出结果是三个被英文句号分割的 Base64 字符串。这些字符串可以容易地在 HTML 和 HTTP 环境中传送的;同时简洁程度好于类似 SAML 那种基于 XML 的标准。
下面展示了一个完整的 JWT。它包含了前面提到的编码后的头部和负载,并且它用了一个密钥来生成签名。
如果你想要试试 JWT,并且把上面提到的概念付诸实践,你可以使用 jwt.io Debugger 来解码、验证和生成 JWT。
在身份验证过程中,当用户使用凭证成功登录的时候,服务器会把一个 JWT 令牌返回给客户端并且 JWT 令牌一定在客户端本地存储(通常是 local storage,但是 cookie 也是可以的),而不是采用传统方法在服务器创建一个 session 并返回给客户端(浏览器)一个 cookie。
用户无论何时想要访问一个受保护的路由或资源,浏览器(译者注:除了浏览器,也可以是其他软件,比如手机 APP。)都要发送 JWT,通常是在使用 Bearer 模式的 Authorization 报头中。报头的内容应该看起来像下面这样:
Authorization: Bearer
因为用户状态从来没有保存到服务器的内存中,所以这是一个无状态的身份验证机制。服务器的受保护的路由将会在 Authorization 报头中检查是否存在一个有效的 JWT 令牌。如果存在有效的令牌,用户将会被允许访问受保护的资源。因为 JWT 是自包含的,所有的必要信息都在 JWT 里,所以减少了多次查询数据库的需求。
这套机制允许你完全依赖无状态的数据 API,甚至允许你向下游服务发起请求。你的 API 用哪个域名都没有关系。因为基于 JWT 的身份验证机制不使用 cookie,所以跨域资源共享(CORS)不会成为一个难题。
当把 JSON 网络令牌(JWT)、简单网络令牌(SWT)和安全断言标记语言令牌(SAML)放在一起比较的时候,让我们谈谈 JWT 的好处。
因为 JSON 没有 XML 那么冗长,所以当 JSON 被转成编码的时候,体积也会更小些。这使得 JWT 比 SAML 更加简洁。在 HTML 和 HTTP 环境下,这些优点促使 JWT 变成一个很好的选择。
在安全方面,SWT 只能对数据签名进行对称加密。SWT 使用了 HMAC 算法,并通过一个共享的密钥来进行对称加密。然而,JWT 和 SAML 令牌能够以 X.509 认证的形式,使用一对公私密钥来对签名进行加密。和给JSON生成数字签名的简便性相比,给XML生成数字签名,既要生成XML格式的数字签名,又要不引入晦涩难懂的安全漏洞是非常困难的。
因为 JSON 可以直接映射成对象,所以 JSON 语法分析器在绝大多数编程语言中都很常用。相反地,XML 没有一个天生的文档到对象的映射。这一点使得应用 JWT 比 应用 SAML 更加简单。
关于用途,JWT 被广泛使用在互联网化的地方。这一点突出了 JWT 在多平台客户端(特别是移动设备)处理上的易用性。
看上面的图,比较一下编码后的 JWT 和编码后的 SAML 的长度。
如果你想要阅读更多关于 JWT 的内容,甚至想在你的应用中使用 JWT 实现身份验证,请浏览 Auth0 网站上的 JWT 登录页面