简介
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,然后服务器检查⽤户是否已经认证过。
这种基于服务器的身份认证⽅式存在⼀些问题:
-
Sessions:每次⽤户认证通过以后,服务器需要创建⼀条记录来保存⽤户信息,通常是在内存中。那么随着认证通过的⽤户越来越多,服务器在这⾥的开销就会越来越⼤。
-
Scalability:由于session是在内存中的,这就带来⼀些扩展性的问题。
-
CORS:当我们想要扩展我们的应⽤,当我们的数据被多个移动设备使⽤时,我们必须考虑跨域资源共享问题。当使⽤AJAX调⽤另⼀个域名下获取资源时,我们可能会遇到禁⽌请求的问题。
-
CSRF:⽤户很容易受到CSRF的攻击
CSRF:跨站请求伪造(早期的攻击⽅式)
假设⼀个⽹站⽤户Bob可能正在浏览聊天论坛,⽽同时另⼀个⽤户Alice也在此论坛中,并且后者刚发布了⼀个具有Bob银⾏链接的图⽚信息。正常情况下,Bob点击图⽚链接访问银⾏,银⾏会让Bob登录
假如Bob在此之前刚好登录过了银⾏,浏览器中还有cookie,那么此时银⾏可能认为点击这个图⽚访问银⾏是Bob发出的,所以会正常给出响应。假如这个链接是⼀个Alice伪造的转账的请求,那么Bob可能就会收到经济损失
-
-
-
基于Token的身份认证
- 基于Token的身份认证,在服务端不需要存储⽤户的登录信息,⼤概流程如下:
- 客户端使⽤⽤户名、密码请求登录
- 服务器收到请求去验证⽤户名和密码
- 验证成功之后服务端会签发⼀个token,再把这个token发送给客户端
- 客户端收到token以后可以把它存储起来,存到客户端内存或者其他地⽅
- 客户端每次向服务器请求资源的时候需要带着服务器签发的token
- 服务端收到请求,然后去验证客户端请求⾥⾯带着的token,如果验证成功,就向客户端返回
请求的数据
- 基于Token的身份认证,在服务端不需要存储⽤户的登录信息,⼤概流程如下:
对比
- 他们都可以存储⽤户信息,然⽽,session是把⽤户信息保存在服务端的,⽽JWT是把⽤户信息保存在客户端的,当然,也可以保存到服务端,甚⾄保存到数据库中。
- Session⽅式存储⽤户信息最⼤的问题在于要暂⽤服务器⼤量的内存,增加服务器的开销。⽽基于token的⽅式将⽤户状态分散到了各个客户端中,可以明显的减轻服务端的内存压⼒。
- 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();