深入理解Session与Cookie

当一个用户通过HTTP访问一个服务器时,这个服务器会将一些Key/Value键值对返回给客户端浏览器,并且加上一些限制条件,当条件符合时,该用户下次访问该服务器数据又会被完整地带回给服务器

实际上考虑的是为了记录用户在一段时间内访问Web应用的行为路径

属性项

当前Cookie有两个版本:Version 0和Version1,两者设置响应头的表示分别是 Set-Cookie 和 Set-Cookie2

两个版本的属性项有相同的部分,也有不同的地方

Version 0属性项:

  1. NAME=VALUE:可以设置要保存的键值对,注意NAME不能和其它的属性项名称一样
  2. Expires:过期时间,设置在该时间后Cookie失效
  3. Domain:生成该Cookie的域名
  4. Path:该Cookies生成的路径
  5. Secure:如果设置了,那么只会在SSH连接时回传该Cookie

Version 1属性项,和0版本一致的不列举:

  1. Version:指定版本
  2. Comment:注释项
  3. CommentURL:服务器为此Cookie提供的URI注释
  4. Discard:是否在会话结束后丢弃该Cookie项,默认为fasle
  5. Max-Age:最大失效时间,这里设置的是多少秒后失效
  6. Port:该Cookie在什么端口下回传服务端,如果有多个端口,以逗号隔开

在Java Web的Servlet规范中并不支持Set-Cookie2响应头,实际上Set-Cookie2的一些属性项可以设置在Set-Cookie中

Cookie的创建与获取

服务端创建Cookie

在创建Cookie时,需要注意以下:

  1. 所创建的Cookie的NAME不能和Set-Cookie或者Set-Cookie2的属性项值一样
  2. 所创建的Cookie的NAME和VALUE的值不能设置成非ASSIC字符,如果要使用中文,可以通过URLEncoder将其编码
  3. 当NAME和VALUE的值出现一些TOKEN字符如”、”,”,”等,构建返回头会将Version自动设置成1
  4. 当在该Cookie的属性项中出现Version为1的属性值时,构建HTTP响应头同样会将Version设置为1

每次创建Cookie时,都是创建一个以NAME为Set-Cookie的MimeHeader

在构建HTTP返回字节流时,是将Header中所有的项顺序地写出,而且没有进行任何修改,所以浏览器在接受HTTP返回的数据时是分别解析每一个Header项的

从客户端获取Cookie

当请求某个URL路径时,浏览器会根据这个URL路径将符合条件的Cookie放在Request请求头中传回给服务端

不同的浏览器对Cookie的数量和大小有不同的限制

Session

使用Cookie的一个缺陷是,每次客户端的访问都需要传回Cookie,如果Cookie较多,则增加了客户端和服务端之间的数据传输量

而Session的出现就是为了解决这个问题,Session传递的是一个ID,这个ID在客户端第一次访问服务端时生成,每个客户端是唯一的。这个ID通常是NAME为JSESIONID的一个Cookie

实际上有三种方法可以让Session正常工作:

  1. 基于URL Path Paramter,默认支持
  2. 基于Cookie,如果没有修改Context容器的Cookies标识,则默认也是支持的
  3. 基于SSL,默认不支持,只有connector.getAttribute(“SSLEnabled”)为TRUE时才支持

Session的工作

如果当前的SessionID没有对应的HttpSession对象,那么就会创建新的,并将其加入sessions容器中保存,只要这个HttpSession对象存在,用户就可以根据SessionID来获取这个对象,也就做到了状态的保持

StandardManager类负责Servlet容器中所有的StandardSession对象的生命周期管理。当容器关闭时(必须调用stop和start命令,不能直接kill进程,会导致容器没有机会调用unload来持久化Session对象),该类会将所有的StandardSession对象持久化到一个”SESSIONS.ser”文件中,当容器启动时会读取该文件

StandardSession对象并不会永远保存,这样会导致容器内存被消耗殆尽,一般来说每个Session对象都会有一个失效时间,Tomcat中一般是60s

针对安全性,Session的安全性要高于Cookie,因为Cookie会将保存的数据通过HTTP头在客户端和服务端之间传递,数据相当于是存放在客户端的。而Session仅通过ID验证身份,数据是存放在服务端的,安全性更高

Cookie压缩

通常的gzip和defate针对HTTPBody的压缩不能压缩在HTTP头部的Cookie,但是如果Cookie的量很大,可以考虑将其也进行压缩。压缩方式是将Cookie的多个k/v看成平台的文本,做文本压缩,压缩算法同样可以使用上述两种。由于Cookie的规范,其中不能包含控制字符,仅能包含ASCII码为34~126的可见字符,所以要将压缩后的结果再进行转码,可以使用Base32或者Base64编码

表单重复提交问题

要防止表单重复提交,就要标识用户的每一次访问请求,使得每一次访问对服务端来说都是唯一的。做法是可以在用户请求一个表单域时增加一个隐藏的表单项,这个这个表单项的值每次都是一个唯一的token。当用户在请求时生成这个唯一的token时,同时将其保存在用户的Session中,当用户提交表单时检查表单token和服务端保存的token是否一致即可