目录
刚刚下班的时候,路上碰到以前带的新人,他说他们那边最近在给一个新系统做新的权限校验,采用JWT的方式,这个新人也做没多久,说自己不了解token、cookie、session的区别,问我有没有时间明天给他讲讲,于是我也萌生了写一下帖子的想法,也是一种整理
cookie
在http刚开始诞生的时候,只是为了满足大家浏览web信息的要求,所以浏览完了就走了,两个连接之间没有任何联系,这也是为什么http是无状态的,毕竟,它诞生的时候就没这种需求
不过,随着网络的发展,web也产生了交互式动作,不只是浏览,还包括了各种登陆啊,各种其他操作,单纯的web是没办法满足需求的,人类之所以进步,就是不断想办法满足自己,这个时候,就需要一种机制来记录每个连接之间的关系,这样,就能够知道当前操作及操作结果的归属者
cookie也会用复数,cookies,由客户端暂时或者永久存储
工作机制如下草图
我们举个例子,假设购物
但是,仔细观察上面这幅图,会发现,随着购物车东西越来越多,cookie也越来越大,那这每一次请求都会很大的负担,如果我单纯增加一个,为啥每次还要把老的数据给过去?用户的数据其实已经记录在服务器上了不是么
session
对上面的改良,由于用户的购物车信息都会存在服务器中,所以cookie其实只要表名了用户的身份,知道是谁发起的购物车变更请求就行了,请求体也只需要带上本次变更的商品信息就行,这样就可以减少session的体积
我们把这种能识别哪个请求由哪个用户发起的机制称为session,也称为会话机制,生成的能识别用户身份的字符串称为session,工作机制如下图
首先用户发起请求,服务端会给用户生成一个唯一的sessionid,与当前用户绑定,然后session通过cookie传给用户
之后每次用户发起请求,只需要携带这个session串,以及自己的请求信息就好了,服务端会通过session找到对应的用户信息,进行对应操作
可以看到,session保存在服务端,但session依赖于cookie传递
注意,session也是有问题的
看似cookie+session没问题了,但是我们这个有一个很致命的前提,那就是服务端是单台,可是实际应用中,为了高可用,都是服务集群,假设请求发到了A服务,A服务生成了session,给了用户,用户第二次请求发到了B服务,B服务根本不认识你是谁
所以,为了解决这个问题,也有三个办法
1、session复制
2、session绑定
3、session共享
首先是session复制,如同字面意思,很简单,一个服务生成了session,那就把这个session赋值给其他所有集群内的服务,缺点:
- 同样的session保存了多份,空间占用上去了
- 节点少还行,节点多了的话,节点间复制成本也十分高昂
其次是session绑定,也就是说,用户A的session是服务B生成的,那么用户A的每一次请求,都发到服务B上,这个也可以通过比如IP什么的规则来实现,但是也有缺点:
- 如果绑定的服务,挂了,你怎么办
最后,session共享,这个也很容易理解,也是现在很多都在使用的方法,可以把session放到DB或者redis中,每次请求来了,服务都去取,当然,缺点就是要多走一步查询,而且为了保证高可用,DB或者redis也都得集群,不过大部分公司都是集群,所以这一点最常用
token
关于上面的session,确实是可以通过session共享的方式来认证用户身份,但是,也有问题,大公司的话其实没什么,但是如果是小公司,或者是个人的使用者,难不成还得专门搞一套高可用的集群么,咱就说,这个成本就高了,业务量达不到,只是为了共享session而去使用,确实是不合适
所以,token就解决了这个问题
首先,用户第一次像服务端发送请求,服务端验证过后,可以生成一个token,客户端拿到token之后,自己保存下来,之后每次请求都携带这个token,服务端会针对这个token进行校验
当然,到这里可能没用过的人会有疑问,那是不是我随便编造一个token给服务端就行?
这个当然不是,服务端是有自己的一套校验机制的,会判断这个token是否是合法的
还有一个疑问,也是一个很重要的点,token怎么知道是哪个用户的?
要回到这个问题,我们先来看看token的结构,这里用JWT的token来做解释
JWT的token分为三个部分:头,载荷,签名
header:指明了签名算法(token头是固定的)
payload:可以用来存储用户id,有效时间等非敏感信息,基于base64加密,所以,千万不要放涉密信息
signature:服务端根据header就知道该使用哪种签名算法,再用密钥根据header+payload生成签名,这样token就生成了
而token的认证过程,就是当服务端收到token时,首先取出header+payload,根据密钥生成签名,再和token中的签名对比,对比成功说明是合法的
要注意,token是有有效期这一个说法的,JWT生成的默认30分钟,想要实现随时失效,可能需要在服务端维护一个黑名单,在校验token之前过一遍黑名单的认证,不过这也就意味着像session一样,黑名单存储在服务端,所以还有一个简单点的实现,那就是客户端登出的时候,直接删了本地的token
还有,token一般放在header的Authorization自定义头里,不是放在cookie里,这样能避免cookie跨站的问题
cookie和token对比
在跨站共享上,cookie不行,要实现的话,需要用比较复杂的技术,而token天然支持
当然,token也有缺点
第一个,太长了,token 是 header+payload 编码后生成的,所以一般比session长,也可能超出cookie的大小,在payload存储的信息越多,token就越长,每次请求都会带上token,也是个不小的负担
第二个,token也有安全风险,我们说了,token生成了就只能等着自动过期,意味着哪怕知道了这个token有危险,也没办法主动让它失效
所以,其实token更适合用来做短期一次认证的场景
具体怎么使用,看各位自己的场景