05-大白话说cookie和session通信到底是怎么一回事
cookie和session说白了就是前后端通信的两个工具,cookie存在于浏览器,session存在于服务端。
前后端通信,拿登录最为典型:
1.首先,前端若想登录,肯定要发送一个字段名证明是你啊,当然了肯定要发送ajax请求,携带信息传给后端。怎么证明是你,比如你是这个网站的会员,你是这个公司的员工,会员有用户名,员工有工号,这些都是证明是你的唯一标识,就拿工号把。如wokerID=123456。
2.然后正常的ajax请求登录接口,不管是get还是post请求(只是传参的位置url和请求体的不一样),带上这个workerID=123456,接着接口请求返回成功后,我们会拿到一个可以保持登录态的,以后访问任何接口都要携带的传给后端的token,那么这个token是怎么来的?
后端要做什么?
后端在接收到用户的请求后,会验证工号对不对,错的话,返回错误状态码和信息给前端,前端告知用户错了。对的话:
后端会通过散列算法或者别的什么算法生成一个签名,目的就是为了确保这个是唯一的,接着:
不管node还是php还是别的语言,都可以设置cookie,并把这个cookie的对应的值就是刚才通过算法得到的签名或者说是密钥。
注意:本质上服务端是无法操作cookie的,服务端是通过设置响应报文,浏览器根据响应报文自行设置的,所以cookie的设置还是浏览器自己的行为。
3.然后呢,因为上面说了,如果服务端设置了cookie,那么浏览器会自动把cookie存储起来,以后浏览器再发请求的时候,会自动携带上cookie(这是浏览器的默认行为,并且是放在请求头中),其实这是服务端传给后端token(也可以说是sessionID)的一种方式。
另一种方式:因为cookie容量比较小,只有4Kb,后端还可以通过前端登录接口请求,以响应内容的方式告诉前端(当然后端不设置cookie也可以这样,只不过前端可以自行拿到token存储在cookie中),前端呢就可以拿到返回来的token存储在localStorage中。
前端拿到这个token就相当于拿到了一个通行证,以后发送任何请求都要携带,证明是你发的,而不是别人随便就可以向后端请求,这也是安全性的一种表现。
前端层面如vue中可以在请求拦截器中统一在header中设置token,这样以后访问所有接口都会自动带上这个头信息,这也相当于后端设置cookie,浏览器自动设置存储,每次请求也是在请求头中自动携带一个道理。
4.最后,服务端在设置cookie的同时(上面说了,也可以不设置),也要设置session,这个session是干啥用的呢?
说白了就是建立与cookie一一对应的关系,那么多员工,那么多用户,怎么知道谁是谁?
php中会自动生成一个返回给前端同名的sessionID(如果是以接口返回那就叫token)文件。
node中也会生成一个文件。存储用户信息。
所以,当不同的用户登录或者请求的时候,服务端就会自动去寻找同名的sessionID文件(token)看是否与前端发来的一致。如果对上号了,就可以接着下面的接口请求,该怎样就怎样了。
直到token被清除掉,登录态就没有了。
下面这些是最好的解释:
基于Session的身份验证
随着交互式Web应用的兴起,比如,购物等需要登录的网站。引出了一个新的问题,那就是要记录哪些用户登录了系统进行了哪些操作,即要管理会话(什么是会话?简单的讲如果用户需要登录,那么就可以简单的理解为会话,如果不需要登录,那么就是简单的连接。),比如,不同用户将不同商品加入到购物车中, 也就是说必须把每个用户区分开。因为HTTP请求是无状态的,所以想出了一个办法,那就是给每个用户配发一个会话标识(Session id),简单的讲就是一个既不会重复,又不容易被找到规律以仿造的随机字符串,使得每个用户的收到的会话标识都不一样, 每次用户从客户端向服务端发起HTTP请求的时候,把这个字符串给一并发送过来, 这样服务端就能区分开谁是谁了,至于客户端(浏览器)如何保存这个“身份标识”,一般默认采用 Cookie 的方式,这个会话标识(Session id)会存在客户端的Cookie中。
虽然这样解决了区分用户的问题,但又引发了一个新的问题,那就是每个用户(客户端)只需要保存自己的会话标识(Session id),而服务端则要保存所有用户的会话标识(Session id)。 如果访问服务端的用户逐渐变多, 就需要保存成千上万,甚至几千万个,这对服务器说是一个难以接受的开销 。 再比如,服务端是由2台服务器组成的一个集群, 小明通过服务器A登录了系统, 那session id会保存在服务器A上, 假设小明的下一次请求被转发到服务器B怎么办? 服务器B可没有小明 的 session id。
可能会有人讲,如果使小明登录时,始终在服务器A上进行登录(sticky session),岂不解决了这个问题?那如果服务器A挂掉怎么办呢? 还是会将小明的请求转发到服务器B上。
如此一来,那只能做集群间的 session 复制共享了, 就是把 session id 在两个机器之间进行复制,但这对服务器的性能和内存提出了巨大的挑战。
因此,又想到如果将所有用户的Session集中存储呢,也就想到了缓存服务Memcached——由于 Memcached 是分布式的内存对象缓存系统,因此可以用来实现 Session 同步。把session id 集中存储到一台服务器上, 所有的服务器都来访问这个地方的数据, 如此就避免了复制的方式, 但是这种“集万千宠爱于一身”使得又出现了单点故障的可能, 就是说这个负责存储 session 的服务器挂了, 所有用户都得重新登录一遍, 这是用户难以接受的。
那么索性存储Session的服务器也搞成集群,增加其可靠性,避免单点故障,但不管如何,Session 引发出来的问题层出不穷。
于是有人就在思考, 为什么服务端必须要保存这session呢, 只让每个客户端去保存不行吗?可是服务端如果不保存这些session id ,又将如何验证客户端发送的 session id 的确是服务端生成的呢? 如果不验证,服务端无法判断是否是合法登录的用户,对,这里的问题是验证, session 只是解决这个验证问题的而产生的一个解决方案,是否还有其它方案呢?
基于Token 的身份验证
例如, 小明已经登录了系统,服务端给他发一个令牌(Token), 里边包含了小明的 user id, 后续小明再次通过 Http 请求访问服务器的时候, 把这个 Token 通过 Http header 带过来不就可以了。
服务端需要验证 Token是自己生成的,而非伪造的。假如不验证任何人都可以伪造,那么这个令牌(token)和 session id没有本质区别,如何让别人伪造不了?那就对数据做一个签名(Sign)吧, 比如说服务端用 HMAC-SHA256 加密算法,再加上一个只有服务端才知道的密钥, 对数据做一个签名, 把这个签名和数据一起作为 Token 发给客户端, 客户端收到 Token 以后可以把它存储起来,比如存储在 Cookie 里或者 Local Storage 中,由于密钥除了服务端任何其他用户都不知道, 就无法伪造令牌(Token)。
如此一来,服务端就不需要保存 Token 了, 当小明把这个Token发给服务端时,服务端使用相同的HMAC-SHA256 算法和相同的密钥,对数据再计算一次签名, 和 Token 中的签名做个对比, 如果相同,说明小明已经登录过了, 即验证成功。若不相同, 那么说明这个请求是伪造的。
这样一来, 服务端只需要生成 Token,而不需要保存Token, 只是验证Token就好了 ,也就实现了时间换取空间(CPU计算时间换取session 存储空间)。没了session id 的限制, 当用户访问量增大, 直接加机器就可以轻松地做水平扩展,也极大的提高了可扩展性。