随着前后端分离的流行,我们的请求开始变成了无状态。

传统的 session 如果不做调整,是没法在这种架构中使用。

但是呢,又不能直接明文的把用户信息,登录状态放到请求里面。

于是便产生了 JWT 。

什么是 JWT?

20220124070621_t8dudu.png

JWT 是 JSON Web Tokens 的缩写。

是为了在网络应用环境间传递声明而执行的一种基于 JSON 的开放标准(RFC 7519)。

该 token 被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。

JWT 的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该 token 也可直接被用于认证,也可被加密。

以上信息,百度到的,看看就行。

JWT 的构成

JWT 的内容由 3 部分组成。

第一部分我们称它为头部(header),第二部分我们称其为载荷(payload,类似于飞机上承载的物品),第三部分是签证(signature)。

像这样:

1
eyJhbGciOiJIU【header部分】.eyJzdWjM0NTY【payload部分】.TJVA9M7E2【signature部分】

每个部分用 . 连接起来,每一部分都使用 Base64 编码。

当我们拿到 token 时,可以取出不同部分,Base64 解码就可以得到元数据。

JWT 的头部承载两部分信息:

  • 声明类型,这里是 jwt
  • 声明加密的算法 通常直接使用 HMAC SHA256

完整的头部就像下面这样的 JSON:

1
2
3
4
{
  'typ': 'JWT',
  'alg': 'HS256'
}

然后将头部进行 base64 加密(该加密是可以对称解密的),构成了第一部分.

1
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

这部分是对这个 Token 的签名方式说明,因为 JWT 他只是一个规范,你可以使用各种不同的签名算法对你的数据进行签名,验签。

为什么要签名?

防止别人对我们返回的数据进行篡改。

所以这个签名算法很关键,如果你用不加盐的 MD5 这类,基本就形同虚设了。

playload

载荷就是存放有效信息的地方。

这里面才是真正的数据,你的用户信息都放在这段里面的。

当然这段也有一些标准,有效信息包含三个部分:

iss: jwt 签发者

sub: jwt 所面向的用户

aud: 接收 jwt 的一方

exp: jwt 的过期时间,这个过期时间必须要大于签发时间nbf: 定义在什么时间之前,该 jwt 都是不可用的

iat: jwt 的签发时间

jti: jwt 的唯一身份标识,主要用来作为一次性 token,从而回避重放攻击。

当然这并不是必须的,根据你个人喜好。

signature

这段是签证信息,非常非常关键的部分。

这关乎你的这个 Token 是否安全,是否能被人仿造。

一般是对 header (base64 后的) 和 payload (base64 后的) 这两部分的数据通过 secret(私钥)进行签名后的结果。

所以这个签名算法就非常关键了,常用的有 SHA256 和 HMAC,我个人更推荐使用 RSA。

安全相关

  • 不应该在 JWT 的 payload 部分存放敏感信息,因为该部分是客户端可解密的部分。
  • 保护好 secret 私钥,该私钥非常重要,如果泄露就会导致别人可以随便仿造你 Token。
  • 如果可以,请使用 https 协议。

缺点

最大的缺点就是他的生命周期不好管控。

换句话说,你签发出去的 Token 如果你不做 Token 黑名单情况下,在有效期内他都是有效的。

所以如果你不做特殊处理,别人是可以复制这个 Token 伪造请求的。

所以一般我们生产中,除了 JWT Token 之外还会配合 CSRF Token 一起使用。