浅谈Cookie与Session

515 阅读10分钟

  前几天接触了一个实现自动登录的项目,其中涉及到了Cookie和Session的相关知识,忽然发现,虽然自己经常使用Cookie和Session,但对两者的原理和联系却知之甚少,特写一篇博客来总结一下。

Cookie

Cookie的产生背景

  在web应用中,会话跟踪是很重要的。一个用户的所有请求操作都应该属于同一个会话。 例如,在网上商城中,用户A购买的商品都应该放在A的购物车内,而不能放在用户B或C的购物车内,因为它们不属于同一个会话。
  众所周知,web应用是使用HTTP协议传输数据的,而HTTP协议是无状态的。这里稍微解释一下HTTP协议的无状态特性:浏览器的每一次请求,服务器都会独立处理,不与之前或之后的请求产生关联,也就是说,浏览器的每一次请求,对服务器来说都是全新的。这个过程可以用下图说明:

图片未加载成功
  一旦数据交换完毕,客户端与服务器端的连接就会关闭,再次交换数据需要建立新的连接,这就意味着服务器无法从连接上跟踪会话。 即用户A购买了一件商品放入购物车内,当再次购买商品时,服务器已经无法判断该购买行为是属于用户A的会话还是用户B的会话了。要跟踪该会话,必须引入一种机制,于是Cookie就应运而生了。Cookie由W3C组织提出,最早由Netscape社区发展,目前所有的主流浏览器都支持Cookie。

Cookie的工作原理

  上面说到由于HTTP协议的无状态特性,服务器无法从连接上知道客户身份。解决办法就是给客户端颁发一个“通行证”,无论谁访问都必须携带自己的通行证,这样服务器就能从通行证上确认客户身份了,这就是Cookie的工作原理。
  Cookie实际上是一小段文本信息。客户端请求服务器,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie,在Servlet中即调用response.addCookie(Cookie cookie)方法。客户端浏览器会把Cookie保存起来,当浏览器再次请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器,服务器检查该Cookie,从而获得用户身份信息。服务器还可以根据需要修改Cookie的内容。

Cookie的机制

  Cookie机制采用的是在客户端保持状态的方案。 当服务器调用addCookie方法向浏览器颁发Cookie,本质上是在HTTP的响应头中加上一行特殊的指示以提示浏览器按照指示生成相应的Cookie,保存在客户端,里面记录着用户当前的信息。当用户再次访问服务器时,浏览器检查所有存储的Cookie,如果某个Cookie所声明的作用范围大于等于将要请求的资源所在的位置,则把该Cookie附在请求资源的HTTP请求头上发送给服务器,服务器根据该Cookie获得用户身份信息。

Cookie相关问题

Cookie的生命周期

  在讨论Cookie的生命周期之前,先说一下setMaxAge(int maxAge)方法,该方法用于设置Cookie的失效时间。
  如果maxAge为正数,则表示该Cookie会在maxAge秒之后自动失效。浏览器会将maxAge为正数的Cookie持久化,即写到对应的Cookie文件中。无论客户关闭了浏览器还是电脑,只要还在maxAge秒之前,登录网站时该Cookie仍然有效。
  如果maxAge为负数,则表示该Cookie仅在本浏览器窗口以及本窗口打开的子窗口内有效,关闭窗口后该Cookie即失效。maxAge为负数的Cookie,为临时性Cookie,不会被持久化,不会被写到Cookie文件中。Cookie信息保存在浏览器内存中,因此关闭浏览器该Cookie就消失了。Cookie默认的maxAge值为–1。

Cookie的修改和删除

  Cookie并不提供修改、删除操作。如果要修改某个Cookie,只需要新建一个同名的Cookie,添加到response中覆盖原来的Cookie即可。
  如果要删除某个Cookie,可以将Cookie的maxAge属性设置为0;也可以新建一个同名的Cookie,并将maxAge设置为0,并添加到response中覆盖原来的Cookie。maxAge为0的Cookie即失效Cookie,会被浏览器从Cookie文件或者内存中删除。
  注意:修改、删除Cookie时,新建的Cookie除value、maxAge之外的所有属性,例如name、path、domain等,都要与原Cookie完全一样。否则,浏览器将视为两个不同的Cookie不予覆盖,导致修改、删除失败。

Cookie的不可跨域名性

  很多网站都会使用Cookie,例如,Google会向客户端颁发Cookie,Baidu也会向客户端颁发Cookie。但是浏览器访问Google只会携带Google的Cookie,而不会携带Baidu的Cookie;Google也只能操作Google的Cookie,而不能操作Baidu的Cookie。这是由Cookie的隐私安全机制决定的,隐私安全机制能够禁止网站非法获取其他网站的Cookie,即Cookie的不可跨域名性。
  Cookie在客户端是由浏览器来管理的,浏览器能够保证Google只会操作Google的Cookie而不会操作Baidu的Cookie,从而保证用户的隐私安全。浏览器判断一个网站是否能操作另一个网站Cookie的依据是域名,Google与Baidu的域名不一样,因此Google不能操作Baidu的Cookie。
  需要注意的是,用户登录网站 www.google.com 之后会发现访问 images.google.com 时登录信息仍然有效,二者虽然同属于Google,但是域名不一样,同样不能互相操作彼此的Cookie,这是因为Google做了特殊处理。想让同一个一级域名下的两个二级域名能交互使用Cookie,需要设置Cookie的domain参数。例如想所有 wsghawk.com 名下的二级域名都可以使用该Cookie,可以这么写:cookie.setDomain(".wsghawk.com"); 注意,domain参数必须以点(".")开始。

Cookie的路径

  domain属性决定允许访问Cookie的域名,而path属性决定允许访问Cookie的路径(ContextPath)。例如,如果只允许/hawk/下的程序使用Cookie,可以这么写:cookie.setPath("/hawk/"); 设置为“/”时允许所有路径使用Cookie,path属性需要使用符号“/”结尾。

Session

Session的工作原理

  Session是另一种记录客户状态的机制,不同的是Cookie保存在客户端浏览器中,而Session保存在服务器上。客户端浏览器访问服务器的时候,服务器把客户端信息以某种形式记录在服务器上,这就是Session。客户端浏览器再次访问时只需要从该Session中查找该客户的状态就可以了。
  如果说Cookie机制是通过检查客户身上的“通行证”来确定客户身份的话,那么Session机制就是通过检查服务器上的“客户明细表”来确认客户身份。Session相当于程序在服务器上建立的一份客户档案,客户来访的时候只需要查询客户档案表就可以了。

Session的机制

  Session机制采用的是在服务器端保持状态的方案。 当用户访问到一个服务器,服务器就要为该用户创建一个Session,在创建这个Session的时候,服务器首先检查这个用户发来的请求里是否包含了一个SessionId,如果包含了一个SessionId则说明之前该用户已经登陆过并为此用户创建过Session,那服务器就按照这个SessionId把这个Session在服务器的内存中查找出来(如果查找不到,就有可能为它新创建一个)。如果客户端请求里不包含有SessionId,则为该客户端创建一个Session并生成一个与此Session相关的SessionId。这个SessionId是一个唯一的、不重复的字符串,这个SessionId将被在本次响应中返回到客户端保存,而保存这个SessionId的正是Cookie。 这样在交互过程中浏览器可以自动的按照规则把这个标识发送给服务器。

Session相关问题

Session的生命周期

  之前我一直认为,一个浏览器窗口被打开是创建一个Session,而关闭浏览器窗口时该Session就会被删除,其实这是错误的! 实际上直到某服务器端程序调用 HttpServletRequest.getSession(true); 这样的语句时Session才被创建。而除非程序通知服务器删除Session,否则Session会被服务器一直保留,直到Session的失效时间到了自动删除。服务器是不知道浏览器是否被关闭的。
  之前会产生这种错觉的原因是:一般Session机制都使用Cookie来保存SessionId,而一旦关闭浏览器,SessionId就不存在了,再连接服务器时就找不到原来的Session了。如果服务器设置的Cookie被保存到硬盘上,或者使用某种手段改写浏览器发出的HTTP请求头,把原来的SessionId发送给服务器,则再次打开浏览器仍然能够找到原来的Session。由于关闭浏览器不会导致Session被删除,迫使服务器为Seesion设置了一个失效时间,当距离客户端上一次使用Session的时间超过这个失效时间时,服务器就可以认为客户端已经停止了活动,才会把Session删除以节省存储空间。
  一般情况下,Session都是存储在内存里,当服务器进程被停止或者重启的时候,内存里的Session也会被清空。如果设置了Session的持久化特性,服务器就会把Session保存到硬盘上,当服务器进程重新启动或这些信息将能够被再次使用(tomcat在shutdown前默认会自动将Session保存到指定的目录中,因此tomcat重新启动后,Session是可以继续使用的)。

Session对浏览器的要求

  SessionId为服务器自动生成的,它的maxAge属性一般为–1,表示仅当前浏览器内有效,并且各浏览器窗口间不共享,关闭浏览器就会失效。因此同一机器的两个浏览器窗口访问服务器时,会生成两个不同的Session。但是由浏览器窗口内的链接、脚本等打开的新窗口(也就是说不是双击桌面浏览器图标等打开的窗口)除外,这类子窗口会共享父窗口的Cookie,因此会共享一个Session。注意:新开的浏览器窗口会生成新的Session,但子窗口除外,子窗口会共用父窗口的Session。
  洋洋洒洒写了不少了,其实目前企业流行的方式还是在请求头中携带token来进行身份验证,因为Session会占用服务器较多的内存空间,影响效率。其实最后还应该写一写Servlet中与Cookie和Session有关的API,但感觉以我这种事无巨细的办事风格又要写很久,还是大家自己去查阅参考文档吧<(^-^)>!
  如对文章内容有疑问或发现其中的错误,请在评论区留言。

参考文章

WEB应用中的SESSION知多少?
Cookie/Session机制详解