理解 JWT

1. 前言

总所周知,HTTP是无状态的,也就是说,当我本次登录之后,下一次仍然需要登录。那这就难受住了┗|`O′|┛ 嗷~~。所以,后来出现了Cookie/Session来保存状态(所说的状态,也就是一些数据)。

但是,使用Cookie/Session也有些问题。比如,在分布式架构下,需要考虑如何同步这些Session,或者集中存储Session等。反正就是在分布式架构下,使用Cookie/Session这种方式来进行用户状态验证很麻烦,要做单点登录也很麻烦。这就促使了JWT的诞生。

之前的Cookie/Session是将用户的状态保存在服务端,现在使用JWT则将用户的状态保存在客户端,你再怎么分布式,也不影响我,扩展性很好。很轻松地就实现了单点登录。

2. 几个概念

JSON Web Token(简称JWT)

JSON Web Token (JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。

加密(Encryption):加密主要分为两类:对称加密非对称加密。对称加密就是使用同一个密钥进行加密和解密;非对称加密就是加密和解密所使用的密钥不同(既可以用公钥加密私钥解密,也可以用私钥加密公钥解密(这也就是签名))。
签名(Signature):举例子吧,A的签名表示,使用A的私钥对某某数据进行加密,这就在该数据进行了签名。签名的作用是表示这个数据的来源是谁。通过A的公钥如果能够解密那就证明数据的来源的确是A(说明这个数据是用A的私钥加密的)。
摘要(Digest):直白讲,就是对数据进行hash之后的值,就叫摘要。摘要的作用是防止数据被篡改,数据改了之后,重新对其进行hash的值与原始的hash值不会相等。

3. JWT的使用场景

  • Authorization(授权):这是JWT最常见的场景。 一旦用户登录,每个后续请求将包括JWT,从而允许用户访问该令牌允许的路由,服务和资源。JWT常用来实现单点登录这一功能,因为它的开销很小,并且使用JWT可以很轻松实现跨域
  • Information Exchange(信息交换):使用JWT可以确保信息传输的安全。因为当使用A的私钥JWT进行签名之后,只要我们能够使用A的公钥解密,这就确定此JWT的发送者一定是A。另外,签名是通过headerpayload计算出来的,我们还可以验证JWT中的内容是否被篡改了。

4. JWT的结构

JWT由三部分组成,由点.分隔,分别是:

  • Header(头部)
  • Payload(负载)
  • Signature(签名)

因此,JWT的格式是:

xxxxx.yyyyy.zzzzz

4.1 Header(头部)

通常,header有两部分组成:token类型(JWT令牌的类型始终是JWT)和使用的签名算法(例如,HMAC SHA256RSA)。

扫描二维码关注公众号,回复: 12683383 查看本文章

示例如下,

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

Base64Url对此JSON编码之后就成为了JWT的第一部分。

4.2 Payload(负载)

JWT的第二部分是payload,其中包含了一些声明(理解为数据就对了)。有3中类型的声明:registered claimspublic claimsprivate claims

  • Registered claims:官方推荐使用一组声明:

    iss(issuer,发布者)
    exp(expiration time,过期时间)
    sub(subject,主题)
    aud(audience,受众)
    nbf (Not Before,生效时间)
    iat (Issued At,签发时间)
    jti (JWT ID,编号)

    值得注意的是,在JWT中声明的名字都是3个字符,这就是JWT的紧凑性的体现。

  • Public claims:可以由使用JWT的人员随意定义,为了避免冲突需要采取一些措施。

  • private claims:这些是自定义声明,目的是在同意使用它们的各方之间共享信息(非registered claimspublic claims的信息`),并且既不是注册声明也不是公共声明。

一个payload的例子如下:

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

使用Base64Url对此payload编码之后就是JWT的第二部分。

注意,JWT 虽然经过签名了,但其默认是不加密的,任何人都可以读到,所以在不加密的情况下,不要把秘密信息放在这个部分。

4.3 Signature(签名)

要创建signature需要:编码后的header、编码后的payload、一个secret(密钥),然后使用header中声明的签名算法来生成签名。

例如,如果你想用HMAC SHA256算法,那么通过如下的方式可以创建signature

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

signature被用来验证信息是否被篡改。如果使用私钥对令牌进行签名,还以验证JWT的发送者是否是你想的那个人。

下图是一个使用密钥签名的JWT的例子:
在这里插入图片描述
使用 jwt.io Debugger可以对其进行解码,验证。如下图所示:

在这里插入图片描述

4.4 Base64Url

这段引自 阮一峰的网络日志 JSON Web Token 入门教程

前面提到,Header 和 Payload 串型化的算法是Base64URL。这个算法跟 Base64 算法基本类似,但有一些小的不同。

JWT 作为一个令牌(token),有些场合可能会放到 URL(比如 api.example.com/?token=xxx)。Base64 有三个字符+、/和=,在 URL 里面有特殊含义,所以要被替换掉:=被省略、+替换成-/替换成_ 。这就是 Base64URL 算法。

5. JWT的工作原理

在认证的时候,如果用户使用他们的凭证(如,用户名/密码)成功登陆后,则会返回一个JWT。由于令牌也是一种凭证,也必须格外小心以防止出现安全问题。 通常,令牌的保留时间不应超过你所需要用它的时间。

由于缺乏安全性,你也不应该将敏感的会话数据存储在浏览器存储中。

无论什么时候,用户想要访问受保护路由或资源时,User-Agent(通常是浏览器)需要带上JWT,通常将JWT放在请求头的Authorization字段中。如下所示:

Authorization: Bearer <token>

服务器上的受保护的路由将会检查 请求头中Authorization字段的JWT是否有效,如果有效,则用户可以访问受保护的资源。另外,当JWT包含一些需要查数据库才有的数据时,可能可以减少对数据库查询。

如果token是在Authorization header(即,上面说的请求头的Authorization字段)中发送的,那么跨源资源共享(CORS)将不会成为问题,因为它不使用cookie

请注意,使用已签名的令牌,令牌中包含的所有信息都会向用户或其他方公开,即使他们无法更改它。 这意味着您不应将机密信息放入令牌中。

6. JWT的特点

此节引用自 阮一峰的网络日志 JSON Web Token 入门教程

(1)JWT 默认是不加密,但也是可以加密的。生成原始 Token 以后,可以用密钥再加密一次。

(2)JWT 不加密的情况下,不能将秘密数据写入 JWT。

(3)JWT 不仅可以用于认证,也可以用于交换信息。有效使用 JWT,可以降低服务器查询数据库的次数。

(4)JWT 的最大缺点是,由于服务器不保存 session 状态,因此无法在使用过程中废止某个 token,或者更改token 的权限。也就是说,一旦 JWT 签发了,在到期之前就会始终有效,除非服务器部署额外的逻辑。

(5)JWT 本身包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了减少盗用,JWT 的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证。

(6)为了减少盗用,JWT 不应该使用HTTP协议明码传输,要使用HTTPS协议传输。

7. 疑问?

7.1 签名是如何来确定信息的发送方的?

信息接收方使用A的公钥来解密信息,如果能解密,就表示信息是由A的私钥加密的,即A发送过来的。

7.2 签名是如何保证信息不被篡改的?

JWT中的签名有两种:
JWT签名 = header + payload,然后通过私钥加密,如果用公钥能解密,则可以验证header、payload是否和签名是对应的。
JWT签名=header + payload + secret,这种签名只能由这个secret所在的服务器来验证(通常,secret并不共享给其他服务器)。
服务端在验证的时候,重新生成一遍签名,如果两次的签名一致,表示信息的内容没有被篡改。

7.3 不同服务器之间如何验证JWT?

  1. 由原secret所在的服务器提供验证API,当此JWT发送给其他服务端后,该服务端调用此API进行验证。那岂不是每请求依次都要调用一次这个API?不是啦,在第一次验证之后,可以将验证结果放到快存,就不用频繁调用API了。
  2. 如果多个服务端之间有较高的信息度,那么原secret服务器可以将secret共享给其他服务端,然后进行验证。
  3. 网上还有说可以使用RS256验证JWT。用「两把」钥匙分别是公钥与私钥以非对称的方法来签证token。

详细情况请移步 Week12 - 要在不同Server间验证JWT好麻烦吗?RS256提供你一种简单的选择 - JWT篇 [Server的终局之战系列]

7.4 JWT与session的差异

相同点是,它们都是存储用户信息;然而,Session是在服务器端的,而JWT是在客户端的。

Session方式存储用户信息的最大问题在于要占用大量服务器内存,增加服务器的开销。

而JWT方式将用户状态分散到了客户端中,可以明显减轻服务端的内存压力。

Session的状态是存储在服务器端,客户端只有session id;而Token的状态是存储在客户端。

7.5 Token的好处是什么?

服务端无状态,服务端的无状态使得服务端更容易扩展。通过token实现认证,避免了用户的用户名和密码被泄露的风险,而且token的生命周期较短,相对较为安全。

7.6 JWT和OAuth的区别

JWT是一种tokenOAuth是一种授权机制。OAuth2用在使用第三方账号登录的情况(比如使用weibo, qq, github登录某个app),而JWT是用在前后端分离, 需要简单的对后台API进行保护时使用。

8. 参考文献

[1] 阮一峰的网络日志 JSON Web Token 入门教程
[2] JWT官网
[3] 五分钟带你了解啥是JWT
[4] Week12 - 要在不同Server间验证JWT好麻烦吗?RS256提供你一种简单的选择 - JWT篇 [Server的终局之战系列]

猜你喜欢

转载自blog.csdn.net/besmarterbestronger/article/details/108479385
jwt