Session & Cookie

引言

进行某活动连续的一段时间内,需要保持服务器与客户端之间会话的状态(登陆了,刷新之后登陆状态依然要保持)。得在服务器与用户之间有一个一一对应的关系。
一个用户的所有请求都应该属于同一会话,会话与会话间相互独立。网站服务一般使用http协议传输数据(无状态协议),一旦数据交换完成,客户端与服务器端的连接就会关闭,再次交换需要再次创建新连接。也就是说,服务器无法从链接上跟踪会话,所以要引入一种机制,来弥补http协议无状态的不足。

一般通过CookieSession来实现。

 - Cookie通过在客户端记录信息,确定用户的身份;

 - Session通过服务器端记录信息,确定用户身份。

长链接 && 无状态

现在被广泛使用的HTTP1.1默认使用长连接。连接方式和有无状态是完全没有关系的两回事。因为状态从某种意义上来讲就是数据,而连接方式只是决定了数据的传输方式,而不能决定数据。长连接是随着计算机性能的提高和网络环境的改善所采取的一种合理的性能上的优化,一般情况下,web服务器会对长连接的数量进行限制,以免资源的过度消耗。

Session没有出现以前,基本所有网站都用CookieCookie 是访问过的网站创建的文件,用于存储浏览信息,例如个人资料信息。这些信息存放在客户端的计算机中,用于客户端计算机与服务器之间传递信息。

无论使用何种服务端技术,只要发送回的HTTP响应中包含如下形式的头,则视为服务器要求设置一个cookie:Set-cookie:name=name;expires=date;path=path;domain=domain支持cookie的浏览器都会对此作出反应,即创建cookie文件并保存(也可能是内存cookie),用户以后在每次发出请求时,浏览器都要判断当前所有的cookie中有没有没失效(根据expires属性判断)并且匹配了path属性的cookie信息,

对于Cookie的实现,每一次http请求都会带给服务器当前域下的Cookie值,服务器通过解析不同的Cookie值,或加密后的Cookie值,来辨识当前用户信息,这些Cookie数据都是存储到客户端,也就是浏览器的键值对,可以删除、更改、设置过期等等,也有一些最大的容量(4kb,20条等)这些限制。浏览器会将这些信息存放在一个统一的位置,对于Windows操作系统而言,我们可以从: [系统盘]:\Documents and Settings[用户名]`Cookie目录中找到存储的Cookie。有两个http头部是专门负责设置以及发送Cookie的,它们分别是Set-Cookie以及Cookie。当服务器返回给客户端一个http响应信息时,其中如果包含Set-Cookie这个头部时,意思就是指示客户端建立一个Cookie,并且在后续的http请求中自动发送这个Cookie到服务器端,直到这个Cookie过期。
Cookie

Session

当程序要为某个客户端的请求创建一个Session的时候,服务器首先检查这个客户端的请求里面是否包含了一个Session的标识,一般我们称之为SessionId。如果已经包含了一个SessionId,就说明已经为当前这个客户端建立过Session,那个服务器就会根据这个SessionId,把这个Session找出来就可以了。如果客户端请求里没有这个SessionId,那就要为这个客户端创建一个Session,并且生成一个跟这个Session关联的一个SessionId,然后这个SessionId的值,应该是一个不会重复,而且不容易被找到规律的字符串,这样以防止被伪造,那么这个SessionId也会在当次的这个响应中返回给客户端保存起来。Session内容只会保存在服务器中,发到客户端的只有Sessionid ;当客户端再次发送请求的时候,会将这个Sessionid 带上,服务器接受到请求之后就会依据Sessionid 找到相应的Session,从而再次使用之。正是这样一个过程,用户的状态也就得以保持了。

Session

保存这个SessionId的方式可以采用Cookie,这样呢,在交互的过程中,浏览器可以自动的按照规则,把标识发送给服务器,一般来说,Cookie的名字都是类似于SessionId,比如依赖于connect的一个express,它的默认的Cookie值就是connect.SessionId,一般情况系下,Session都是存储到内存里,到我们的开发情况下,当服务器进程被停止了,或者重启了,内存里的Session也会被清空,如果设置了Session持久化的特性,比如服务器吧Session保存保存硬盘上,那么当服务器进程重启后,这些信息就能再次被使用。

那么对于Session的持久化,有几种常见的方式,比如基于Cookie/基于内存/基于redis/基于mongoDB.

  • 基于Cookie,把数据直接存储到Cookie里,解析后就能获得
  • 基于内存,使用内存当容器来存储数据
  • 基于redis,把数据存入到redis数据库中,这样不同进程都能获取到Session数据,Cookie解析后获取到的SessionId,通过SessionId访问内存里面,再来获取相应的数据。
  • 基于mongoDB同上。

持久化
在默认情况下,HTTPS连接的cookie会自动加上secure这个参数。

HttpOnly Cookie机制

HttpOnly是指仅在HTTP层面上传输的Cookie,当设置了HttpOnly标志后,客户端脚本就无法读写该Cookie,这样能有效的防御XSS获取Cookie

cookie和session的区别:

  1. Session保留在服务器中,cookie保留在客户端或浏览器中
  2. 在Session机制中,session是通过标示符(session id)来识别用户身份,维持会话的,所以session依赖于session id, 后者的保存方式采用cookie,在交互的过程中浏览器自动的按照规则把标识符发送给服务器,当cookie被用户禁止时,也可以采用URL重写的技术,即把session id附加在URL路径后面,附加的方式有两种,一种是作为URL路径的附加信息,一种是作为查询字符串附加在URL后面。

什么情况下会导致客户端JavaScript读取Cookie失败?

  1. 服务器对其设置了 HttpOnly。服务端设置了http-only属性的Cookie,客户端JS无法读取,更别说更改了。(HttpOnly是指仅在HTTP层面上传输的Cookie,当设置了HttpOnly标志后,客户端脚本就无法读写该Cookie,这样能有效的防御XSS获取Cookie)。

  2. 域名限制。跨域的Cookie会存取失败(跨二级域名不包括在内)。

  3. 客户端可以自由禁止。如果浏览器设置了阻止网站设置任何数据, 客户端无法接收Cookie,当然JS对Cookie的操作会失败。

  4. 大小限制。Cookie的数量超过最大限制,之前的Cookie被自动删除,JS无法读取到。

  5. Cookie过期被浏览器自动删除了。

优缺点 && 应用场景

  1. 应用场景
    Cookie的典型应用场景是Remember Me服务,即用户的账户信息通过cookie的形式保存在客户端,当用户再次请求匹配的URL的时候,账户信息会被传送到服务端,交由相应的程序完成自动登录等功能。当然也可以保存一些客户端信息,比如页面布局以及搜索历史等等。
    Session的典型应用场景是用户登录某网站之后,将其登录信息放入session,在以后的每次请求中查询相应的登录信息以确保该用户合法。当然还是有购物车等等经典场景;
  2. 安全性
    cookie将信息保存在客户端,如果不进行加密的话,无疑会暴露一些隐私信息,安全性很差,一般情况下敏感信息是经过加密后存储在cookie中,但很容易就会被窃取。而session只会将信息存储在服务端,如果存储在文件或数据库中,也有被窃取的可能,只是可能性比cookie小了太多。
    Session安全性方面比较突出的是存在会话劫持的问题,这是一种安全威胁,这在下文会进行更详细的说明。总体来讲,session的安全性要高于cookie;
  3. 性能
    Cookie存储在客户端,消耗的是客户端的I/O和内存,而session存储在服务端,消耗的是服务端的资源。但是session对服务器造成的压力比较集中,而cookie很好地分散了资源消耗,就这点来说,cookie是要优于session的;
  4. 时效性
    Cookie可以通过设置有效期使其较长时间内存在于客户端,而session一般只有比较短的有效期(用户主动销毁session或关闭浏览器后引发超时);
  5. 其他
    Cookie的处理在开发中没有session方便。而且cookie在客户端是有数量和大小的限制的,而session的大小却只以硬件为限制,能存储的数据无疑大了太多。

session原理

它的基本原理是服务端为每一个session维护一份会话信息数据,而客户端和服务端依靠一个全局唯一的标识来访问会话信息数据。用户访问web应用时,服务端程序决定何时创建session,创建session可以概括为三个步骤:

  1. 生成全局唯一标识符(sessionid);
  2. 开辟数据存储空间。一般会在内存中创建相应的数据结构,但这种情况下,系统一旦掉电,所有的会话数据就会丢失,如果是电子商务网站,这种事故会造成严重的后果。不过也可以写到文件里甚至存储在数据库中,这样虽然会增加I/O开销,但session可以实现某种程度的持久化,而且更有利于session的共享;
  3. 将session的全局唯一标示符发送给客户端。
    问题的关键就在服务端如何发送这个session的唯一标识上。联系到HTTP协议,数据无非可以放到请求行、头域或Body里,基于此,一般来说会有两种常用的方式:cookie和URL重写。
  4. Cookie
    读者应该想到了,对,服务端只要设置Set-cookie头就可以将session的标识符传送到客户端,而客户端此后的每一次请求都会带上这个标识符,由于cookie可以设置失效时间,所以一般包含session信息的cookie会设置失效时间为0,即浏览器进程有效时间。至于浏览器怎么处理这个0,每个浏览器都有自己的方案,但差别都不会太大(一般体现在新建浏览器窗口的时候);
  5. URL重写
    所谓URL重写,顾名思义就是重写URL。试想,在返回用户请求的页面之前,将页面内所有的URL后面全部以get参数的方式加上session标识符(或者加在path info部分等等),这样用户在收到响应之后,无论点击哪个链接或提交表单,都会在再带上session的标识符,从而就实现了会话的保持。读者可能会觉得这种做法比较麻烦,确实是这样,但是,如果客户端禁用了cookie的话,URL重写将会是首选。

    参考: