关于jwt的思考
jwt是个做用户权限认证的方案,科普的内容参考相关文档吧,这里提出几个关于jwt的思考:
现有项目架构:
pc项目->pc服务器->api服务器
app项目->api服务器
1. jwt是否需要服务器存储用户状态
按照jwt的思路,服务端是不需要存储用户状态的,只要有秘钥+过期时间就可以实现用户的认证和过期,至于读库vs加解密验证哪个过程对服务器的压力更大,这个可能需要对比测试,但从原理和常识来讲,存库读库的成本应该更高一点。无库产生的加解密开销相对于读写库应该是非常小的,而且无库可以轻松做到跨服务器的认证。
2. jwt方案中token的安全问题
分为两个安全问题:
token泄露,泄露一般存在两个地方,1是传输层,2是客户端。先考虑传输层:http被窃听,或者https被破解(https的安全性这里不做讨论),那么如果token在传输层被拦截,传统的cookie存储的sessionId也有一样的问题。再说客户端,一样的情况,如果cookie可以被窃取,token也没办法解决这个问题。已经做过的测试是:拦截到cookie后,把cookie放在另一台电脑上也是可以使用的,这个跟token是一样的。
还有一种情况是重放攻击,http请求被拦截重放,这个安全性对于cookie和jwt是一样的,避免这个问题需要配合其他手段,但基本很难避免。
综上,jwt碰到的安全问题跟传统方式一样。
3. jwt如何解决续签问题
传统的cookie的续签方案一般都是框架自带的,session有效期30分钟,30分钟内如果有访问,session有效期被延长30分钟。那么jwt是怎么实现的呢,细节没看完,不过基本的逻辑是频繁重签名(不知道为什么有人这么干,开销应该不小,而且问题挺多),或者是有效期30分钟,服务器发现token要过期了,提前三分钟重签并返回新的token,客户端每次请求都检测新旧token,如果不一致更新本地token(这个可以放在http的请求预处理里,复杂度和开销都不大)。关于新token的发放导致旧token有效期内的访问失效的问题参见这里的token新旧更替问题讨论 。这种方案就可以实现浏览器里传统cookie的续签需求,以及app7天内登陆就可以持续刷新登陆状态的问题(app比较特殊,每天第一次访问时刷新一下token)。
4. jwt如何解决注销问题
好了,jwt最大的问题来了,怎么注销?先看传统的session是怎么注销的:用户点击退出,调用后台退出接口,也就是session注销接口,依托于后台的状态更新。那么服务端做的呢,如果是用文件或者数据库存储的,那么通过删记录是可以后台强制踢出的,放在内存里就不要想了(如果用户量大了放内存的话服务器也吃不消,放文件里io太频繁基本也不靠谱,还好有了Redis)。jwt最大的问题就在于后台没有存储用户状态,用户退出的话只是客户端删掉了token,然而此token在有效期内还是有效的,也就是说如果token泄露的话就麻烦了,不过token泄露的问题已经在上面讲过了,和cookie是同一个问题。那么最麻烦的就是怎么让一个token在用户注销后失效,以及后台强制退出,这个jwt是没办法的,因为jwt的无状态和用户状态维护是个矛盾冲突的话题。如果要解决就要建立一个黑名单,也就是把用户注销后的token放到redis里,然后每次校验黑名单(这里还是用到了数据库),
5. 使用jwt+Redis黑名单的方案
接上个问题,jwt推崇的无状态和跨服务器认证在数据库的出现后,貌似没有任何优势,真的没有优势吗?也不全是,至少有两个小优势,第一,存储黑名单(也就是注销后的用户或者黑户)比起存储登录之后的用户这个量级都是相当小的,存储量下降&读取速度提高。第二个优势是app和pc能共用同一套认证机制,也就意味着前后端彻底的分离,不必再为cookie和session认证单独搭一个服务器。
6. 用了jwt之后html服务怎么提供呢
对于前后端分离的项目来说,pc服务器的两个任务1是做认证(jwt已经解决),2是提供html服务。html服务可以交给nginx或者放到cdn,无论哪个方案都比tomcat之类的静态资源处理能力更强大,也就是说html和js等静态资源全部交给静态资源服务器来处理。那么安全问题又来了,怎么对文件做权限控制呢?对于一个前后端分离的项目来说,html一般不会嵌套后端的代码,所以html没有所谓的安全问题,换句话说html里根本没有任何有价值的内容。对于js来说,里面包含了业务逻辑,实际上对js做限制也是没有任何意义的,因为前端的代码是暴露给客户端的,即便控制,黑客登录之后也是能看到你的代码的,惟一能做的就是压缩混淆,把压缩后的代码放到线上,源码不放到线上,最大程度增加通过阅读js读取业务逻辑的难度(实际意义也不大,尤其对于有些公司压根连压缩这一步都不做的,更没有这个问题的顾虑了)。那么也就是说前后端分离的项目里html(没有后端代码嵌入)和js是没有安全的考虑必要的,真正要考虑的大概也就是图片的防盗链和pdf等静态资源的处理吧,这个因为对于后端不是很熟悉所以不做深入讨论,对于pdf等资源能想到的也就是通过需要授权的接口去拿pdf的路径吧。
7. 到底要不要数据库
如果不考虑用户注销后一定要token失效和强制退出需求的话,是不需要数据库的,即便加上redis对于传统方案来说也是有上文提到的两个优势。而且其实注销后一定要token失效主要也是考虑token泄露的问题(见上文),所以如果有踢出用户登录需求的话,还是要有数据库做黑名单存储的,一旦用户被拉入黑名单,就要立马停掉用户的签名发放,所以这个问题并不单单是认证的问题,还有签名发放的问题。
8. 用户认证的终极解决方案(跨服务器)
前提就是跨服务,实际上jwt本身已经实现了跨服务(无状态),既然服务端不存储用户状态,只要多个服务器之间使用共同的秘钥和加密方案就可以实现x.abc.com里登录之后在y.abc.com那台服务器做登录认证。但是问题上面也说了,如果不考虑注销和强制退出的情况的话,这个话题也就到处为止了。但是如果完美主义呢?当然还是要实现这两个功能,也就是要有黑名单服务器,或者说认证服务器auth.abc.com,无论x.abc.com还是y.abc.com里登录或者注销都要先通过auth.abc.com,但是带来的读写不一致和额外的认证开销问题大概也是一个历史性难题吧。或许也可以参考Oauth2.0的方案,做隐式认证。不存储用户的状态的话,jwt跨服务器应该是个比较简单的解决方案吧,但如果一个公司有跨服务器需求的话,那么规模之大,里面的后端童鞋应该有更为专业的方案吧,欢迎分享。此段不专业莫要踩我。
结论:
上文阐述了作为一个卑微的前端能想到的jwt的问题和思考,总结来说,使用jwt可以实现真正的前后端分离,浏览器可以和app用一样的认证机制,无论对服务器的搭建还是服务器开销都是不错的优化,至于黑名单的设置,这个看需求吧。
彩蛋
为什么会有彩蛋呢,因为结论里不能说太多废话,就搁这里说了,jwt主要解决了浏览器和app共用一个服务器的问题,也就是少了一个pc服务器。
有看客就要问了,那你怎么看node呢,ssr?pwa?字节码?
这个当然要且看下回分晓了️️��