JWT(JSON Web Token)是目前最流行的认证方案之一。博客园、各种技术公众号隔三差五就会推一篇JWT相关的文章,真的多如牛毛。但我对JWT有点困惑,今天写出来跟大家探讨探讨,不要喷哈。
JWT原理
本文默认读者已经对JWT有所了解,下面不再详细介绍JWT,只简单提一下。
JWT全称JSON Web Token。当服务器认证成功后会生成一个Token,这个token包含了header、payload、signature三部分信息。其中payload的内容有过期时间、签发时间、还有自定义的字段。自定义字段往往用来存放用户信息,比如UserId,UserName等等信息。当客户端收到这个token后存储在Cookie,localstorage或者别的什么地方并且以后每次请求都带上token。服务端对请求所携带的token进行解析,判断是否过期是否合法。
以上简单的描述了下JWT的工作原理,因为jwt的payload携带了过期时间、用户信息等,所以JWT有别于传统Session方案的一个最大不同就是JWT是无状态的,JWT不用在内存或DB里维持session的状态,直接拿到token解析就可以了。
JWT的优点
无状态?
这个优点真的爽,因为没有了session,不用考虑session服务器的压力所以可以毫无顾忌的水平扩展,个人认为这是JWT最大的一个优点,也是JWT的核心内容。但是这也带来了一个致命的问题:无法让单独某个用户(token)过期或者失效,恰恰这又是一个非常非常常用的功能。
为了解决这个问题,网上提出一些方案:比如服务端设置一个blacklist或者配合redis来存储token跟过期时间,每次请求到服务端解析JWT之后再次去blacklist或者redis里查询一次看看是否已经注销或者已经过期。
但是。。。这样不就又把session请回来了吗?这样的方案跟我用sessionId去取session又有啥区别呢?所谓session不一定非要是asp.net mvc又或者springmvc自带的session管理叫做session,任何带有中心存储功能能维持状态的东西都是session,比如上面方案里的redis就是一个确确实实的session。
跨域?
因为传统基于cookie的session机制sessionid存在cookie里,但是cookie不能跨域。但是JWT把token放在http的一个Authorization header上传输所以就可以轻松跨域。
但是sessionId就一定要存在cookie下吗,sessionId同样也可以存储在localstorage里,然后请求的时候携带在http的某个header上,事实上cookie本身也是通过http的一个header传输的。这样不就同样可以跨域了吗?sessionId跟token有区别吗?个人认为没有区别,都只是一个字符串而已。jwt怎么在客户端存储放在哪个header上那么sessionId就同样可以。
数据更安全?
JWT的签名也仅仅是仿篡改,把数据直接存储在客户端,尽管可以加密(JWT加密不是必须的),但是显然谈不上安全。如果是一串无意义的sessionId,它不存储数据,又不能篡改,是不是更安全呢?
预防CSRF?
这个通跨域那个解释一样,sessionId不一定非要存储在cookie中。
总结
为了预防被喷,再次强调下。今天写下这不是为了喷JWT。JWT本身设计没有什么问题。真正无状态的JWT确实可以带来实实在在的好处,服务端水平扩容变的异常容易,再也不用担心session复制的效率问题,也不用担心session挂掉后整个集群全部无法正常工作的问题,确实是一个实实在在的好东西。但是,好东西就一定大家都需要吗?个人认为如果您所要开发的系统并发量不是那么高,对水平扩展没那么高的需求,并且对用户注销是刚需,那么请好好考虑下是否真的需要JWT。或许简单的sessionId配合一个存储工具比如redis,更能符合你的要求。如果你的程序并发高,用户量大,实时在线人多,那么使用真无状态JWT是一个非常好的选择。它能够让你从容的水平扩容,它能够让你省下不少session服务器的费用,session服务不再是您系统的瓶颈。但是这样的系统又有多少?
关注公众号一起玩转技术吧