JWT(JSON Web Token) 原理介绍

自从 RESTful 架构兴起后,越来越多的人提倡使用 JWT 来取代传统 Session 的场景,究竟什么是 JWT 呢,来了解下吧~

JWT 介绍

  1. JWT 是 JSON Web Token 的缩写,是一种开放标准(RFC 7519),即基于 JSON 对象的编码,并通过这个编码传递信息。
  2. JWT 会通过 HMAC、RSA、ECDSA 等算法进行加密。
  3. 通常利用 JWT 来对用户进行验证,也就是说用户会先请求身份凭证服务器拿到该JWT,然后,只要用户携带这个 JWT 向业务服务器请求资源,如果这个 JWT 是有效的,那么就能获取资源。

JWT 的组成

JWT 的结构可以看作是三个 JSON 对象,并且用点(.)来分隔,这三个部分会各自进行编码,组成一个 JWT 字符串。也就是变成:xxxxx.yyyyy.zzzzz

Header(头部)

由两部分组成:

  1. alg(算法) 也就是 token 被加密的算法,如 HMAC、SHA256、RSA。
  2. typ(类型) 也就是 token 的类型,基本上就是 JWT。

示例:

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

然后进行 Base64 进行编码。Base64 是通过 64 个字符来表示二进制数据的一种方法,编码的方式是固定的而且可以逆向解码的,并不是那种安全的加密算法。

Payload(负载)

这里放的是声明(Claim)内容,也就是用来传递消息的地方,在定义上有三种声明:

1. Registered claims

可以看作是标准公认的一些消息,建议放,但并不强迫,例如:

  • iss(Issuer):JWT 签发者
  • exp(Expiration Time):JWT 的过期时间,过期时间必须大于签发 JWT 时间
  • sub(Subject):JWT 所面向的用户
  • aud(Audience):接收 JWT 的一方
  • nbf(Not Before):也就是定义拟发放 JWT 之后,的某段时间点前该 JWT 仍旧是不可用的
  • iat(Issued At):JWT 签发时间
  • jti(JWT Id):JWT 的身份标识,每个 JWT 的 Id 都应该是不重复的,避免重复发放

2. Public claims

这个,可以看作是传递的字段必须与上面的 Registered claims 字段不能冲突,然后可以向官方申请定义公开声明,会进行审核等步骤,实际上在开发上是不太会用这部分的。

3. Private claims

这是发放 JWT 服务器可以自定义的部分,例如实际上会放 User Account、User Name、User Role 等不敏感的数据。

所谓不敏感的数据就是不会放用户的密码等敏感数据,因为该 Payload 传递的消息最后也是通过 Base64 进行编码,所以是可以被破解的,因此放用户密码会有安全性的问题。

示例:

{
  "sub""1234567890",
  "account""kenny@example.com",
  "role""admin"
}

个人感觉通常都会放 iat、exp 等标准字段,因为通常需要检查 JWT 发送时间及是否过期,以及还有用户账号,为了方便查询用户的一些数据,通常以前的做法是 Session 里面存放用户账号,现在改用 JWT 的 payload 上存放,以及角色身份的定义,可以用来看该用户是否有权限获取后端 API 的数据。

Signature(签名)

由三大部分組成:

  • base64UrlEncode(header)
  • base64UrlEncode(payload)
  • secret

也就是:

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

header 和 payload 中间用点(.)来连接,secret 是存放在服务器端的加密字符串,最后将这三个部分串接再一起的字符串进行加密算法进行加密。

 

secret 是要保存在服务器端的,这个 secret 一旦泄露给客户端,客户端就可以自己生成 JWT,并通过该 JWT 访问资源,因此 secret 是永远不该泄露的。

最后将 Header、Payload、Signature 三者用点(.)拼接在一起,就是一个合法签发的 JWT 字符串。在 JWT 官网:https://jwt.io/ 有提供现成的 JWT 签发工具,可以使用该工具来看产生的 JWT 字符串长什么样:

我使用上面我写的三个部分的内容各自填上去,左边就会出现正确的 JWT 字符串出来。红色部分就是编码后的 Header、紫色部分就是编码后的 Payload,蓝色部分就是将 Header、Payload、Secret 合在一起并进行加密算法加密后的编码。最后就是用点(.)将三个部分串联在一起。

客户端如何用 JWT 来访问资源?

  1. 前端会先通过访问后端的登录 API,后端验证用户账号密码成功后,就会发放合法 JWT 字符串。
  2. 前端拿到 JWT 字符串就会将 JWT 存放在 Local Storage 里面。
  3. 而后当前端要访问受保护的资源 API 时,只要在 Header 的填写以下内容:
Authorization: Bearer 
  1. 后端收到后,会去检查 Authorization 的 JWT token 是否有效,如果有效,则允许前端访问受保护的资源。在以前的 Session 的设计上,Session 会存放在 Redis 等这种缓存数据库,每当用户访问受保护的资源时,会先去存储数据库的 Session 进行比对,有效则让用户访问,以 JWT 的方式可以降低查询数据库的需求。

JWT 的优缺点

优点:

  • 采用 JSON 对象的形式,大部分的编程语言都支持
  • 可以存放一些用户信息,但并非是敏感的信息
  • 整个 JWT,只要 Payload 不要放过多的信息,其实 Size 是相当小的
  • 不用在 Server 的数据库存放 Session,特别适合多台 Server 的场景下,使得扩展性容易,因为多台 Server 要使用 Session 的话,会有共享 Session 的问题发生
  • 对于现在手机上的 APP 的应用特别好,用户不用每次打开 APP 都要重新输入账号与密码
  • 支持跨域请求,不会有传统用 Cookie 进行跨域请求等的问题

缺点:

  • JWT 没办法主动失效,也就是说不能像 Session 一样被强制无效,但是个人觉得这有很多方式可以避免
  • JWT 一旦泄露会有很严重的安全性问题,但是泄露通常可以通过两种方式:
  1. 黑客使用你的电脑,并得知 JWT 这...你电脑都被攻陷了...那就不好说了
  2. 使用中间人攻击的方式,截取客户端传服务器端的数据包,并获取 JWT,但使用 HTTPS 传输可以大幅度降低该攻击,只要定期更换 SSL 证书就可以了

总结

JWT 之所以会兴起,除了因为 RESTful 架构出现,加上现在微服务的架构的关系,一般来说上线的系统,不太可能用单台服务器来处理一切,多台服务器处理 Session 会有其局限性,虽然可以用统一的数据库进行存放 Session 来控制,但是会有性能的问题。

不过其实这还有很多探讨的空间,在这篇文章没有去说,Session/Cookie 的架构其实也是可以跟 JWT 并行。但就我两者都使用过的经验来看,我觉得 JWT 的方式的确很简洁易懂,Session 的机制有时候的确很繁琐。

 

阅读剩余
THE END