session和cookies会话机制详解

session management 会话管理的原理


web 请求与响应基于 http,而 http 是无状态协议。所以我们为了跨越多个请求保留用户的状态,需要利用某种工具帮助我们记录与识别每一次请求及请求的其他信息。举个栗子,我们在淘宝购物的时候,首先添加了一本《C++ primer》进入购物车,然后我们又继续去搜索《thinking in java》,继续添加购物车,这时购物车应该有两本书。但如果我们不采取 session management 会话管理的话,基于 http 无状态协议,我们在第二次向购物车发出添加请求时,他是无法知道我们第一次添加请求的信息的。所以,我们就需要 session management 会话管理!


会话管理的基本方式

会话管理的基本主要有隐藏域,cookies,与 URL 重写这几种实现方式。用得较多的是后两种。


隐藏域实现会话管理

以一个网络注册信息填写为例。
我们在填注册信息的时候,经常遇到填完一个页面的内容之后,还要继续填写下一个页面的内容。但由于 http 的无状态,那么容易造成的后果,当进入第二页填写的时候,服务器已经不记得我们上一页填写了什么。

怎么利用隐藏域解决这个问题呢?
顾名思义,其实就是既然服务器不会记得两次请求间的关系,那就由浏览器在每次请求时主动告诉服务器多次请求间的必要信息,但是上一页的信息并不显示在第二页中,而是采用隐藏域的方式。
然而显然这种方式是存在各种问题的。
比如关掉网页之后,就会遗失信息,而且查看网页源代码时,容易暴露信息,安全性不高。隐藏域并不是 servlet/jsp 实际会话管理的机制。

cookie 是什么?举个简单的例子,现在当我们浏览网站的时候,经常会自动保存账号与密码,这样下次访问的时候,就可以直接登录了。这种技术的实现就是利用了 cookie 技术。cookie 是存储 key-value 对的一个文件,务必记住,它是由服务器将 cookie 添加到 response 里一并返回给客户端,然后客户端会自动把 response 里的 cookie 接收下来,并且保存到本地,下次发出请求的时候,就会把 cookie 附加在 request 里,服务器在根据 request 里的 cookie 遍历搜索是否有与之符合的信息
具体 cookie 的实现我们会在后面详细讲到

URL 重写实现会话管理

URL 重写就是将需要记录的信息附加在请求的链接背后,以链接参数的形式发送给服务器识别。具体实现的过程会在后文结合 cookie 详解。


servlet&jsp 中的 session 会话管理机制

利用 httpsession 对象进行会话管理。httpsession 对象可以保存跨同一个客户多个请求的会话状态。

换句话说,与一个特定客户的整个会话期间看,httpsession 会持久储存。

对于会话期间客户做的所有请求,从中得到的所有信息都可以用 httpsession 对象保存。

httpsession 的工作机制

  • 以之前的问卷调查为例,当一个新客户小明填写问卷时,服务器会生成一个 httpsession 对象,用于保存会话期间小明所选择的信息,服务器会以 setAttribute 的方式将其保存到 httpsession 对象中。
    每个客户会有一个独立的 httpsession 对象,保存这个客户所有请求所需要保存的信息。
  • 服务器如何识别所有的请求是否来自同一个客户?
    客户需要一个会话 ID 来标识自己。就跟我们每个人的身份证号一样。对于客户的第一个请求,容器会生成一个唯一的会话 ID,并通过相应把它返回给用户,客户在以后发回一个请求中发回这个会话 ID,容器看到 ID 之后,就会找到匹配的会话,并把这个会话与请求关联。
  • 实现存储会话 ID 的就是通过 cookie!

image.png

cookie 存储在客户端,是被服务器放在 response 里发回客户端的,以后每次 request 时,都会把 cookie 加入到 request 里。
而 session 是存在服务器的,以属性的形式将会话中的信息存到 httpsession 对象中。调用时,只要通过 httpsession 对象调用相应 attribute 即可。

  • 很多地方总是把 session 与 cookie 分开单独讲。但我们通过前面的介绍,不难知道,session 实现其会话管理机制时,在如何确定所有请求是否来自同一个客户时,是利用了 cookie 技术的。所以不应该将 cookie 与 session 完全分开讲。

  • 这里产生这个误解的原因。是因为我们对 session 的会话管理机制不够了解。因为容器在创建 session 对象时,会帮我们实现所有 cookie 相关的工作,而我们只需要实现这一句:

1
HttpSession session = request.getSession();

记住: 这个方法不只是创建一个会话,而是会完成所有与 cookie 相关的工作,只是容器都自动帮我们实现了。我们来看看容器在背后默默为我们做了什么:

  • 建立新的 httpsession 对象
  • 生成唯一的会话 ID
  • 建立新的会话对象
  • 把会话 ID 与 cookie 关联
  • 在响应中设置 cookie

cookie 所有的工作都在后台进行。
看到这里,是不是很爽?容器几乎帮我们实现了所有 cookie 工作。
从请求中得到会话 ID
只需一行代码:

1
HttpSession session = request.getSession();

与上一部分为响应生成会话 ID 是一致的
其中也在后台实现了一些步骤:
if(请求包含一个会话 ID)
找到与该 ID 匹配的会话
else if(没有会话 ID 或者没有匹配的 ID)
创建一个新的会话。
还是那句话: cookie 所有工作都在后台自动进行

cookie 原先设计的初衷就是为了帮助支持会话状态。但是因为 cookie 的简便性,容器为我们封装了大量操作。现在 cookie 已经被越来越运用到各个方面。
首先,我们明确 cookie 是存在客户端的,实际上就是在客户端与服务端交换的一小段数据(一个 name/string 对)。
由于 session 在用户关闭浏览器后,会话结束,就会消失,cookie 随之应该也会消失。但 servlet 的 API 中提供了一些方法,可以让客户端的 cookie 存活的时间更久一点。这就是 cookie 相对于 session 的一大优势所在。我们目前常用的记住用户名和密码,下次登录就是利用 cookie 在 session 消失后,还能存活实现的。
所以,我们可以定制 cookie 为我们实现各种功能。