一文理清Session,Cookie和Token
前言
之前看了不少同类型的文章,对于Cookie和Session真是众说纷纭,有的人说先有Cookie才有Session,也有的人说现有Session再有Cookie。我也说说我的理解,希望对大家有所帮助。
注:需要读者对于Cookie/Session/Token有基本的了解。
Cookie和Session
说这俩之前,先说http,它是一个无状态协议。
怎么理解呢?就是说连续的两次请求之间是没有任何关联的。这样的好处是快速,坏处是想要把同个域名下的两个页面关联起来,必须使用某些手段和工具。
这就引出了我们要说的Cookie和Session。
让我们用文字+图片了解一下客户端访问服务器的流程:
• 首先,客户端会发送一个http请求到服务器端;
• 服务器端接受客户端请求后,建立一个Session,并发送一个http响应到客户端,这个响应头,其中就包含Set-Cookie头部。该头部包含了SessionId;
• 在客户端发起的第二次请求,假如服务器给了Set-Cookie,浏览器会自动在请求头中添加Cookie;
• 服务器接收请求,分解Cookie,验证信息,核对成功后返回response给客户端。
这里有几点要注意的:
• Cookie只是实现Session的其中一种方案。虽然是最常用的,但并不是唯一的方法。禁用Cookie后还有其他方法存储,比如放在url中;
• 单一使用Session不用Cookie,或者反过来只用Cookie不用Session,在理论上都可以保持会话状态,只是实际操作中因为多种原因,大多都是Session + Cookie;
• 用Session只需要在客户端保存一个id,实际上大量数据都是保存在服务器端。如果全部用Cookie,数据量大的时候客户端是没有那么多空间的;
• 如果只用Cookie不用Session,那么账户信息全部保存在客户端,一旦被劫持,全部信息都会泄露。并且客户端数据量变大,网络传输的数据量也会变大。
简单总结,就是Session 有点像用户信息档案表, 里面包含了用户的认证信息和登录状态等信息,而 Cookie 就是用户通行证。
Token
Token 也称作令牌,由uid+time+sign(+固定参数)组成:
• uid: 用户唯一身份标识
• time: 当前时间的时间戳
• sign: 签名, 使用 hash/encrypt 压缩成定长的十六进制字符串,以防止第三方恶意拼接
• 固定参数(可选): 将一些常用的固定参数加入到 Token 中是为了避免重复查库
Token 的认证方式类似于临时的证书签名, 并且是一种服务器端无状态的认证方式, 非常适合于 REST API 的场景. 所谓无状态就是服务器端并不会保存身份认证相关的数据。
Token在客户端一般存放于LocalStorage,Cookie,或SessionStorage中。在服务器一般存于数据库中。
Token认证流程
Token 的认证流程与Cookie很接近
• 用户登录,成功后服务器返回Token给客户端;
• 客户端收到数据后保存在客户端;
• 客户端再次访问服务器,将Token放入Headers中;
• 服务器端采用filter过滤器校验。校验成功则返回请求数据,校验失败则返回错误码。
Cookie,Session和Token的区别
Token可以抵抗CSRF攻击,Cookie+Session不行
我们假设一个场景:
用户正在登陆银行网页,同时登陆了攻击者Tom的网页;
银行网页未对CSRF攻击进行防护;
Tom在网页放了一个表单,该表单提交src为http://www.bank.com/api/transfer,body为count=1000&to=Tom。
如果用的是Session+Cookie,用户打开网页的时候就已经被Tom转走卡里的钱了。因为form 发起的post请求并不受到浏览器同源策略的限制,因此可以任意地使用其他域的 Cookie 向其他域发送 post 请求,形成 CSRF 攻击。在post请求的瞬间,Cookie会被浏览器自动添加到请求头中。
但Token不同,Token是开发者为了防范CSRF而特别设计的令牌,浏览器不会自动添加到Headers里,攻击者也无法访问用户的Token,所以提交的表单无法通过服务器过滤,也就无法形成攻击。
分布式情况下的Session和Token
我们已经知道Session是有状态的,一般存于服务器内存或硬盘中,当服务器采用分布式或集群时,Session就会面对负载均衡问题。
• 负载均衡多服务器的情况,不好确认当前用户是否登录,因为多服务器不共享Session。这个问题也可以将Session存在一个服务器中来解决,但是就不能完全达到负载均衡的效果。有办法解决但是挺麻烦的。
而Token是无状态的,Token字符串里就保存了所有的用户信息
• 客户端登陆传递信息给服务器端,服务器端收到后把用户信息加密(Token)传给客户端,客户端将Token存放于LocalStroage等容器中。客户端每次访问都传递Token,服务器端解密Token,就知道这个用户是谁了。通过cpu加解密,服务器端就不需要存储Session占用存储空间,就很好的解决负载均衡多服务器的问题了。
注:这个方法叫做JWT(Json Web Token)。
总结
• Cookie类似一个令牌,装有SessionId,存储在客户端,浏览器通常会自动添加。
• Session存储于服务器,可以理解为一个状态列表,拥有一个唯一识别符号SessionId,通常存放于Cookie中。服务器收到Cookie后解析出SessionId,再去Session列表中查找,才能找到相应Session。同时Session依赖Cookie。
• Token可以说是一个无状态的令牌,需要开发者手动添加。用户信息被加密到Token字符串中,服务器收到Token后解密就可知道是哪个用户。
• JWT只是一个跨域认证的方案。
撰写
Eolinker:致力于API全生命周期开发管理的国产流行API工具。
参考文章
Cookie、Session、Token那点事儿
cookie,token验证的区别
有了cookie为什么需要session
彻底弄懂session,cookie,token
CSRF Token的设计是否有其必要性
cookie,token,session三者的问题和解决方案
负载均衡集群中的session解决方案
JWT介绍
Json Web Token 入门教程