简介

JWT全称 Json·Web·Token,是⼀个开放标准(RFC·7519),它定义了⼀种紧凑的,⾃包含的⽅式,⽤于作为JSON对象在各⽅之间安全的传输信息。该信息可以被验证和信任,因为它是数字签名的。
JWT是⽬前最流⾏的跨域身份解决⽅案。

使用场景

  • Authorization(授权): 这是使⽤JWT的常⻅场景。⼀旦⽤户登录,后续每个请求都将包含JWT,允许⽤户访问该令牌允许的路由、服务和资源。单点登录是现在⼴泛使⽤的JWT的⼀个特性,因为它的开销很⼩,并且可以轻松的跨域使⽤。

跨域:⽐如你的⽹站有⼏个不同的后台的域名,你在a域名下登录,登录信息记录到cookie,那么后⾯访问a域名才会带cookie,其他域名是不会带cookie的。

因为浏览器的同源策略:

先来看看什么是同源?
同源:如果两个 URL 的 protocol、port (如果有指定的话)和 host 都相同的话,则这两个URL 是同源。

举例,假如现在有⼀个url:http://www.csdn.net/dir/page.html

url 结果 原因
http://www.csdn.net/dir2/other.html 同源 只有路径不同
http://www.csdn.net/dir/inner/another.html 同源 只有路径不同
https://www.csdn.net/secure.html 不同源 协议不同
http://www.csdn.net:81/dir/etc.html 不同源 端口不同
http://mp.csdn.net/dir/other.html 不同源 主机不同

同源策略是⼀个重要的安全策略,它⽤于限制⼀个origin的⽂档或者它加载的脚本如何能与另⼀个源
的资源进⾏交互。它能帮助阻隔恶意⽂档,减少可能被攻击的媒介。

因为有了浏览器同源策略的限制,所以有了跨域问题。

单点登录:Single Sign On,简称为 SSO,是⽐较流⾏的企业业务整合的解决⽅案之⼀。SSO的定
义是在多个应⽤系统中,⽤户只需要登录⼀次就可以访问所有相互信任的应⽤系统。

Information Exchange(信息交换):对于安全的在各⽅之间传输信息⽽⾔,Json·Web·Token⽆疑是⼀种很好的⽅式。因为JWT可以被签名,例如,⽤公钥/私钥配对,可以确定发送⼈就是他们所说的那个⼈。另外,由于签名是使⽤头和有效负载计算的,还可以验证内容有没有被篡改。

如何解决单点登录问题(集群为例)

  • 方案1:Nginx负载均衡做iphash,同⼀个⽤户的请求永远只给到同⼀个后台服务器。

    不⾜之处:⼀个服务有若⼲个⽤户,有的⽤户活跃,向后端发起请求的次数⽐较频繁,⽽有的⽤户不活跃,那么基于这种iphash的解决⽅案的话就势必会造成负载不均衡,也就是说有的tomcat服务服务器压⼒⼤,⽽有的tomcat服务器压⼒不⼤,造成资源分配不均的情况。其次就是假如有⼀个tomcat服务挂了,那么对于有⼀些⽤户来说,这个系统就挂了,失去了集群的意义。

  • 方案2:基于tomcat⼴播的session复制

    不⾜之处:每⼀个tomcat都需要维护⼀个⼤的session,会造成内存资源紧张。

  • 方案2:让每一个Tomcat服务器,将其自身的session信息,都存储到一个共享数据库中,也可以解决这个问题,但是这样一来,我们原本只需在内存中访问的session对象,就得去数据库中访问了

    速度降低,访问数据库获取数据比直接访问内存获取慢了很多

解决⽅案:使⽤JWT能够完美的解决上述问题

Token VS Session

在看JWT如何解决上述问题之前,我们先来看看基于Token的身份认证与基于session的身份认证。

  • 基于session的身份认证。

    • HTTP协议是⽆状态的,也就是说,如果我们已经认证了⼀个⽤户,那么他下⼀次请求的时候,服务器不知道我是谁,我们必须再次认证。传统的做法是将已经认证过的⽤户信息存储到服务器上,⽐如session。⽤户下次请求的时候带着sessionId,然后服务器检查⽤户是否已经认证过。

      这种基于服务器的身份认证⽅式存在⼀些问题:

      1. Sessions:每次⽤户认证通过以后,服务器需要创建⼀条记录来保存⽤户信息,通常是在内存中。那么随着认证通过的⽤户越来越多,服务器在这⾥的开销就会越来越⼤。

      2. Scalability:由于session是在内存中的,这就带来⼀些扩展性的问题。

      3. CORS:当我们想要扩展我们的应⽤,当我们的数据被多个移动设备使⽤时,我们必须考虑跨域资源共享问题。当使⽤AJAX调⽤另⼀个域名下获取资源时,我们可能会遇到禁⽌请求的问题。

      4. CSRF:⽤户很容易受到CSRF的攻击

        CSRF:跨站请求伪造(早期的攻击⽅式)
        假设⼀个⽹站⽤户Bob可能正在浏览聊天论坛,⽽同时另⼀个⽤户Alice也在此论坛中,并且后者刚发布了⼀个具有Bob银⾏链接的图⽚信息。正常情况下,Bob点击图⽚链接访问银⾏,银⾏会让Bob登录
        假如Bob在此之前刚好登录过了银⾏,浏览器中还有cookie,那么此时银⾏可能认为点击这个图⽚访问银⾏是Bob发出的,所以会正常给出响应。假如这个链接是⼀个Alice伪造的转账的请求,那么Bob可能就会收到经济损失

  • 基于Token的身份认证

    • 基于Token的身份认证,在服务端不需要存储⽤户的登录信息,⼤概流程如下:
      1. 客户端使⽤⽤户名、密码请求登录
      2. 服务器收到请求去验证⽤户名和密码
      3. 验证成功之后服务端会签发⼀个token,再把这个token发送给客户端
      4. 客户端收到token以后可以把它存储起来,存到客户端内存或者其他地⽅
      5. 客户端每次向服务器请求资源的时候需要带着服务器签发的token
      6. 服务端收到请求,然后去验证客户端请求⾥⾯带着的token,如果验证成功,就向客户端返回
        请求的数据

对比

  1. 他们都可以存储⽤户信息,然⽽,session是把⽤户信息保存在服务端的,⽽JWT是把⽤户信息保存在客户端的,当然,也可以保存到服务端,甚⾄保存到数据库中。
  2. Session⽅式存储⽤户信息最⼤的问题在于要暂⽤服务器⼤量的内存,增加服务器的开销。⽽基于token的⽅式将⽤户状态分散到了各个客户端中,可以明显的减轻服务端的内存压⼒。
  3. session的状态存储在服务端,客户端只有sessionId,⽽token的状态是存储在客户端

使用Token的优点:

  • ⽆状态和可拓展性:Token 存储在客户端,完全⽆状态,可拓展。我们的负载均衡器可以将⽤户传递到任意服务器,因为在任何地⽅都没有状态或会话信息。
  • 安全:Token不是cookie。每次请求的时候token都会被发送,可以作为请求参数发送,可以放在请求头⾥⾯发送,也可以放在cookie⾥⾯被发送。即使在你的实现中将token存储到客户端的cookie中,这个cookie也只是⼀种存储机制,⽽⾮身份认证机制。没有基于会话的信息可以操作,因为我们没有会话。

JWT token的基本格式

JSON·Web·Token由三部分组成,他们之间⽤圆点(·)连接,这三部分分别是:

  • Hea er
  • Payload
  • Signature

Header由两部分信息组成:

  • type:声明类型,这⾥是jwt

  • alg:声明加密的算法 通常直接使⽤ HMAC SHA256

Payload就是存放有效信息的地⽅(不强制)

  • iss: jwt签发者
  • sub: jwt所⾯向的⽤户
  • aud: 接收jwt的⼀⽅
  • exp: jwt的过期时间,这个过期时间必须要⼤于签发时间
  • nbf: 定义在什么时间之前,该jwt都是不可⽤的
  • iat: jwt的签发时间
  • jti: jwt的唯⼀身份标识,主要⽤来作为⼀次性token,从⽽回避重放攻击
  • claim:jwt存放信息的地⽅
  • Signature就是签名信息

因此,⼀个典型的JWT看起来是这个样⼦的:
xxxxxxxx·yyyyyyyyyy·zzzzzzzzzzz
具体如下:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ3bGd6cyIsImV4cCI6MTU4Nzk3MzY1N
ywidXNlciI6Ijk2MkYxODkwNTVFMzRFNzVERjVGMzQ0QTgxODNCODdGIn0.APehq9dxRiilgTOGyuz
9qtZxvPDIJ5QIIVUCLYeX1QE

所做项目中已经整合好了JWT,提供了现有的工具类进行操作

com.mall.user.utils.JwtTokenUtils

使⽤现有的⼯具创建JWT:

String token = JwtTokenUtils.builder().msg("xxx").build().creatJwtToken();

解析JWT:

String msg = JwtTokenUtils.builder().token(token).build().freeJwt();