jwt介绍
jwt(JSON Web Token)本身是一个行业标准,用于端与端之间安全的将信息作为json对象传输,核心思想是通过散列算法(不可逆算法)做签名,从而验证消息是否被篡改过,和https签名算法是一样的原理。
jwt官网介绍
jwt常用的散列算法(sha算法)与java实现
jwt做登录的由来
最一开始做登录,就是将登录后生成唯一token返回,同时将token和用户对应起来存在数据库,以后这个用户操作的时候,就得带着这个token,服务器通过查询数据库,就知道这个token是对应的哪个用户了。
像这种将用户和token保存在服务端的做法,其实已经可以解决所有问题了。
但随着有人发现,使用jwt,可以不用保存token,也可以达到验证用户的效果,那当然最好了,和数据库交互当然是越少越好。
所以,使用jwt做的登录,就是登录后用jwt签名生成特殊格式的token(token中至少包括用户信息和签名信息)返回,不保存数据库,以后用户操作的时候,带上token,服务器就拿着用户信息对签名信息做算法验证,验证通过说明token有效。
jwt核心token解析
接下来详细说下这个用jwt签名生成特殊格式的token:
这个token是一个字符串,用.隔开,总共有三部分:Header.Payload.Signature
Header是头信息json格式通过base64编码生成的字符串,头信息一般存放着生成token的算法,因为生成token的散列算法有多种,所以需要在头信息中指出是哪个。
Payload是荷载信息,其实就是存放一些系统自定义信息,比如用户标识,也是json格式通过base64编码生成的字符串。
Signature是签名字符串,是将Header.Payload字符串+系统内定的**secret,通过散列算法生成的签名信息。
借一张官网原图:
jwt为什么可以是无状态的
分析一下为什么jwt可以做到不用服务端存储,就能验证是不是已登录的用户:
首先,Payload中,都会存放用户信息,比如用户名,所以验证的时候,服务器端就知道是哪个用户了。
那么,万一这个token信息是被人篡改过呢,由于token的第三部分Signature是通过散列算法得到的,也就是不可逆的,Signature=散列算法(Header.Payload,系统内定的**secret),所以只要Header.Payload被改过,服务器端重新算出的Signature就和传过来的Signature对不上了。
同时,服务器端这个系统内定的**secret是最重要的,只要这个不被泄露出去,这个token就绝对安全。这个其实也是提醒各位设计系统的小伙伴,如果你的系统是一个很重要的系统,那就别用jwt了,因为万一你这个系统内定**泄露,任何人就都有可能模拟用户登录了。
网上的错误使用案例
看完这些,咱们再说说,网上的一些误人子弟的教程:
比如,当你搜索jwt redis的时候,就会发现有很多人都在写,由于jwt是无状态的,所以服务器端如果服务器端不存储token,就没法做token过期的功能,比如用户登录的token,过30分钟不用就自动失效的功能。
于是,有个骚操作就被想出来了,甚至是很多人都这样做了,那就是选择在服务器端再用redis缓存用户,并加上过期时间,那么每次token验证的时候,再验证一下redis中的用户key是否过期就好了。
讲真的,这么做的这些人就不懂jwt。因为我们用jwt的本意,就是为了不和数据库交互,那你再加个redis,那用jwt还有啥优势,那还不如直接生成唯一token,记录在redis中,不是更好么。
其实要做token失效,也有不用redis的做法,这个做法也是我想出来的,那就是在Header.Payload.Signature的Payload中,加入过期时间,每次验证的时候都验证一下当前时间是否大于过期时间就好了,但这种做法,至今没在网上看到过。