JWT 梳理

Header: 存类型还有加密方式,是 Base64 编码

Payload: 存 JSON 对象,传递需要的数据,但是不能放敏感信息,比如密码,是 Base64 编码

Signature:前两部分 base64 后加密钥

一、token 生成原理

1. 生成

  1. 将荷载 payload,以及 Header 信息进行 Base64 加密,形成密文 payload 密文,header 密文。
  2. 将形成的密文用句号连接起来,服务端秘钥进行 HS256 加密,生成签名。
  3. 将前面的两个密文后面用句号链接签名形成最终的 token。

2. 服务端验证

  1. 用户请求时携带此 token(分为三部分,header 密文,payload 密文,签名)到服务端,服务端解析第一部分(header 密文),用 Base64 解码,可以知道用了什么算法进行签名,此处解析发现是 HS256。
  2. 服务端使用原来的秘钥与密文(header 密文+”.”+payload 密文)同样进行 HS256 运算,然后用生成的签名与 token 携带的签名进行对比,若一致说明 token 合法,不一致说明原文被修改。
  3. 判断是否过期,客户端通过用 Base64 解密第二部分(payload 密文),可以知道荷载中授权时间,以及有效期。通过这个与当前时间对比发现 token 是否过期。

二、服务端如何让 token 失效?

这个其实并没有一个很好的方案。

  • 比如维护一个 token 和 user 的对应关系,存数据库里(但这样和 session 其实是一样的,还不如用 session)
  • 再比如维护一个黑名单,黑名单里存本身没有过期但又要令其无效的 token。(这个方案较为合适,但是这样其实和 JWT 的无状态原则是相互违背的,服务端还是要存东西,虽然存的数据并不多)

所以说其实并没有一个非常好的方案让 token 失效。如果想要保证无状态原则,那只能将 token 过期的时间尽可能缩短。

无状态

  • 服务端不需要维护这个状态,数据都是存在客户端,节省服务端资源,因此没有分布式 Session 一致性问题。
  • 符合 RESTful API 原则(无状态)

安全性(预防 CSRF)

CSRF 原理见 web 安全-CSRF

CSRF 攻击之所以能够成功,是因为攻击者可以完全伪造用户的请求,该请求中所有的用户验证信息都是存在于 Cookie 中,因此攻击者可以在不知道这些验证信息的情况下直接利用用户自己的 Cookie 来通过安全验证。

要抵御 CSRF,关键在于在请求中放入攻击者所不能伪造的信息,并且该信息不存在于 Cookie 之中。

token 由服务端生成的,可以加载请求头里,如果没有被获取到,是没办法请求成功的。如果被获取到了,那就相当于发生了 XSS,已不属于 CSRF。

多平台跨域(单点登录)

单点登录 JWT 有天然优势,见下面方案。

A.com 和 B.com 如何共享登陆状态?

两种方案

方案一:cookie-session 方式

增加一个用户认证中心 SSO.com

  • 用户访问 A,没有 session 连接,重定向到 SSO,并将自身地址作为参数 ?server=A.com
  • 进入 SSO,没有 session 连接,引导至 A 的登陆页面
  • 用户填写账号密码后,向 SSO 发起登陆, ?username=xiaoqi&password=123&server=A.com
  • SSO 登陆后,创建 session 会话,并保存至浏览器 cookie(此时是 SSO 和用户之间的会话),并重定向到 A,并携带 token ?token=xxxxxxx
  • A 拿到 token,向 SSO 认证,如果正确则创建 session 会话,保存 cookie(此时是 A 和用户之间的会话),此时 A 已经登陆了
  • 此时,用户访问 B,没有 session,重定向到 SSO,此时检测到已经登陆(SSO 与用户之间有会话),重定向到 B,?token=xxxxxxx
  • B 拿到 token,向 SSO 认证,如果正确则创建 session 会话,保存 cookie(此时是 B 和用户之间的会话),此时 B 已经登陆了
  • 至此,A 和 B 已经和用户建立了会话,同时 SSO 也和用户建立了会话。但是可以看到一个问题,A 和 B 的 session 不一样,也就是说是两个会话,因此其实假如 A 的 session 过去了,但是 B 可能还没有过期。同时还有 Session 分布式问题,以及单点注销等各种麻烦的问题。

方案二:JWT 方式

使用 JSON Web Token,服务端不需要记录状态,验证成功后返回 token,存入 localStorage 中。因此上面的各种问题都不存在了,只需要解决一个问题,localStorage 如何共享?使用 postMessage,登陆后传递给 iframe,然后 iframe 拿到后存入 localStorage 中,这样 A 和 B 就能实现 localStorage 共享。

和传统的 token 有什么区别?

token 中组成:用户标识(uid)+ time(时间戳)+ sign(签名)

因此需要在数据库中校验

而 JSON WEB Token 则可以无需查库,因为所需要的信息都存在 payload 中了,直接判断 payload 即可