为什么使用JWT?

通过 session 管理用户登录状态成本越来越高,因此慢慢发展成为 token 的方式做登录身份校验,然后通过 token 去取 redis中的缓存的用户信息,随着之后jwt的出现,校验方式更加简单化,无需 通过 redis 缓存,而是 直接 根据 token 取出保存的用户信息,以及对 token 可用性校验,单点登录更为简单。

公钥和私钥

是通过一种 算法 得到的一个 密钥对 = 公钥 + 私钥

  • 公钥:向外界公开
  • 私钥:自己保留
  • 通过算法得到的 密钥对 能保证在世界范围内是唯一的。使用这个密钥对的时候,如果用 其中一个密钥 加密 一段数据,必须用 另一个密钥解密

    比如用公钥加密数据就必须用私钥解密,用私钥加密必须用公钥解密,否则解密将不会成功。

JWT 数据格式

  1. Header:对头部进行Base64Url编码(可解码),头部包含两部分信息:
    声明类型(JWT):"typ": "JWT",
    加密算法(自定义):"alg": "HS256"
  2. Payload:对载荷(有效数据)进行Base64Url编码(可解码),载荷(有效数据)的7个示例信息
    (JSON形式):
    1. iss (issuer):表示签发人 (举例如下):"iss": "Oline JWT Builder"
    2. exp (expiration time):表示token过期时间
    3. sub (subject):主题
    4. aud (audience):受众
    5. nbf (Not Before):生效时间
    6. iat (Issued At):签发时间
    7. jti (JWT ID):编号
  3. Signature:签名,是整个数据的认证信息。一般根据前两步的数据,再加上服务的的密钥 secret (密钥仅仅为保存在服务器中,并且不能向用户公开,即 私钥),通过 Header 中配置的加密算法 alg 生成。用于验证整个数据完整和可靠性。
    HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload),secret )

最终生成的JWT数据格式如下图:

ex1:

ex2:

注意:这里的数据通过 . 隔开成了三部分;另外,这里数据是不换行的,图片换行只是为了展示方便而已。

JWT交互流程

流程图:

ex:

因为JWT签发的 token 中已经包含了用户的身份信息,并且每次请求都会携带,这样服务的就无需保存用户信息,甚至无需去数据库查询,这样就完全符合了RESTful的无状态规范。

JWT 存在的问题

  1. 续签问题,传统的cookie+session的方案天然的支持续签,但是jwt由于服务端不保存用户状态,因此很难完美解决续签问题,如果引入redis,虽然可以解决问题,但是jwt也变得不伦不类了。
  2. 注销问题,由于服务端不再保存用户信息,所以一般可以通过修改secret来实现注销,服务端secret修改后,已经颁发的未过期的token就会认证失败,进而实现注销,不过毕竟没有传统的注销方便。
  3. 密码重置,密码重置后,原本的token依然可以访问系统,这时候也需要强制修改secret。
  4. 基于第2点和第3点,建议不同用户取不同secret。