JWT(JSON WEB TOKEN)的特点是无状态,通常用来作为验证登录以及鉴权,在这一方面,JWT体现出了他的优点。
然而,如果使用JWT实现,用户主动注销、强制登出(禁止登陆)、忘记密码、修改密码,JWT续签等方面的需求,就会让人头疼。
按照网上的一些讨论,我概括如下:
1、将每一个签发的JWT保存在REDIS上,当出现上述的需求时,就更新对应的JWT,如果客户端与REDIS中的不同,那么登录失败。
这种方法虽然可以解决上述问题,但是此时JWT似乎变成了有状态的了,部分失去了JWT的优点。
2、在签发、鉴别JWT中,不使用公共的秘钥,而是为每一个账户保存一个对应的秘钥,当客户端发来请求时,使用秘钥验证,如果失败说明这个JWT已经失效,可以实现上述的功能。
然而,对于每一个请求都需要在数据库中查找对应的秘钥,这为服务器增加了负担。
3、如果用户主动注销,或者主动修改密码,仅仅在客户端对之前的JWT删除(此时,如果获得了之前的JWT仍然可以登录),似乎不会对安全性带来影响,因为此时,之前的JWT并没有被泄露。
然而有没有一种方式可以不使用数据库来实现上述功能?
这个问题的答案我也不清楚。
不过希望我提供的这一个方案可以给大家一些思路。(如有雷同纯属巧合)
使用JWT的一个原则是尽可能的发挥它无状态的优点。
一般来说,在Restful的操作中,用户发生正常操作的概率,远远大于上述5个操作。为了充分利用这一点,减少查库操作,我把上述五个功能分为2个部分
可预知:用户主动注销、修改密码、以及JWT续签
可预知:服务器在当前请求中,知道当前操作的一个效果是为了失效当前JWT,在这个部分中,服务器清楚地知道需要失效的JWT的实际值。
解决方案:当服务器收到一个可预知的请求时,首先在redis中查询是否存在当前JWT,如果存在返回false,否则在redis中插入当前jwt,这条数据的失效时间为当前jwt的剩余有效时间。
不可预知:强制登出(禁止登陆)、忘记密码、
不可预知:服务器在当前请求中,知道当前操作的一个效果是为了失效某个JWT,这个JWT的实际值未知,但是这个JWT所对应的账户已知。
解决方案: 解析这个JWT后,得到AppID,timeStamp、查询redis,如果对应AppID 的timeStamp小于 redis中的timeStamp,返回false,否则在redis中插入当前AppId,与当前时间戳,这条数据的失效时间为JWT生存期。(因为无法得到签发的JWT剩余时间,只能按照最大时间处理)。
上述方法缺点:还是避免不了查库,但是减少了库中存放的内容
如果上述方法存在漏洞,欢迎指正。