贯通Java Web开发三剑客
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

1.5 Cookie和Session

读者可能在平时上网、阅读某些Internet方面的资料或者在一些可以开发Web应用的开发语言中已经不止一次地看到过Cookie和Session这两个概念,因为这两个概念在Web中确实起到了举足轻重的作用。正如前面在介绍HTTP协议时提到的,HTTP协议是无状态协议,协议本身不会保存任何对方的状态信息,但是在许多时候服务器会希望保存一些必要的客户端信息或者希望能够在客户端保存一些信息,以便在客户端下次访问时携带上这些信息。这就是Cookie机制和Session机制出现的原因。这两种技术都不是HTTP协议规定的内容,但是它们被广泛使用于Web系统中。

1.5.1 Cookie

Cookie是Web服务器要求客户机浏览器保存在客户机本地的一个非常小的文本文件,该文件的内容由服务器指定;同时服务器还指定了一个URL集合,当浏览器下次访问这些URL中的任何一个时都会携带上Cookie中的内容。Cookie中的内容实质上是一系列属性,属性由属性名和属性值组成,属性名和属性值都是由Web服务器指定的。

Cookie的实现非常简单,却有着旺盛的生命力和广泛的应用。一个简单应用Cookie的例子是页面可以记录用户登录名:当用户登录某网站时,会输入用户名和密码,当用户登录成功后,Web 服务器会将用户的登录名使用Set-Cookie头域设置到客户机本地硬盘,并要求客户机在用户下一次打开登录页面时携带用户的登录名信息,Web 服务器就可以取出用户的登录名信息并且将其设置到用户名输入框中。这个过程的HTTP消息序列如表1.8所示。

表1.8 Cookie工作过程

在这个例子中,最关键的就是第4步中的Set-Cookie头域和第6步的Cookie头域。第4步中服务器告诉客户机浏览器要将username=zhangsan这么一 个名值对记录到本地Cookie中,第6步浏览器在下次访问该网页时将该名值对携带在HTTP消息中以便于服务器获得该信息。在Set-Cookie头域的值中,除了定义需要携带的属性信息外,还定义了expires属性、domain属性和path属性。其中expires表示过期时间,即表示这个Cookie设置到该日期以后就无效了。domain属性和path属性合起来规定了一系列URL,即只要访问的URL的域名是example.com,Request-URI是以/login开始的话,那么就将该Cookie携带在HTTP请求头中。

1.5.2 Session

考虑一个简单的情形:邮件服务器中每个用户的邮件内容应该是受密码保护的,在没有使用正确的用户名和密码登录后是不能访问的。用户的邮箱中有许多资源,例如邮件列表、邮件内容、通讯录等,由于每个资源都应该受密码保护,所以用户在每一次请求任何一个资源时都必须输入用户名和密码,否则别人就有可能通过构造该资源的URL访问到该资源。这是因为HTTP是无状态协议,一次请求中的登录信息无法被其他请求使用。但是,如此频繁的输入用户名和密码对于用户来说是无法容忍的。

这种情形在Web应用中是普遍存在的,因为有很多Web应用都需要身份验证。Session机制是一种服务器端的机制,它可以解决上面提到的情形:当用户登录成功时,服务器通过一种特殊的算法生成一个不会重复并且很难找到规律的字符串,称为Session ID,并且将Session ID保存在本地Session ID库中(使用一种类似于散列表的结构来保存信息),同时通过某种机制将Session ID告诉给客户机;当服务器接收到任何访问受保护资源的请求时,只需要查看请求中是否已携带 Session ID 并且检查携带的Session ID是否已在Session ID库中存在,假如存在则表示该客户机已成功登录,否则拒绝该请求。

1.Session ID的传递方式

Session ID是Session机制的关键,只有通过将Session ID在服务器和客户机之间传递才能够实现Session的作用。Session ID最常用的传递方式是利用Cookie进行传递。

当用户登录成功后,服务器将 Session ID 作为一个 Cookie 的属性发送给客户机,并且适当设置domain和path,使得客户机在访问需要受密码保护的资源时都携带上该Cookie属性。例如:

    ...
    Set-Cookie: sessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99
          zWpBng!-a; expires: Mon, 31-Mar-2008 00:00:00 GMT; domain: example.com;
          path: /mail
    ...
    ...
    Cookie: sessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99
          zWpBng!-a
    ...

除了Cookie的方式,也可以通过URL来传递Session ID。当用户登录成功后,服务器将在每个需要访问受密码保护资源的URL中都加入Session ID,例如:

    http://www.example.com/mail?sessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-a

2.Session ID的过期

关于Session ID过期的问题,常常会有一种误解:“只要关闭浏览器,Session就消失了”。其实简单考虑就可以明白这是一种误解,Session是一种服务器机制,Session ID是由服务器生成并且保存在服务器本地的,对于任何一个携带Session ID的请求,只要携带的Session ID在服务器本地的Session ID库中存在,就可以访问受保护的资源,也就是说明Session ID是有效的。可见,Session ID是否过期与客户机并没有任何关系,而只与服务器本地的Session ID库中有哪些Session ID有关,Session ID库中不存在的Session ID 视为无效;而当客户机关闭浏览器时通常并不会通知服务器,所以服务器也不会主动删除该Session ID。恰恰是由于关闭浏览器不会导致Session被删除,故服务器才需要为Session设置一个失效时间,当服务器发现客户端停止活动的时间超过这个失效时间时,就会把对应的Session ID删除。

Session 的本意是“会话”的意思,会话表示一组顺序而且相互关联的对话过程。HTTP 是无状态协议,假如将 HTTP 的一次请求/响应过程看作是一次对话,那么无状态就表示每一次对话和其他对话之间都是没有关联的,即本次对话时已经忘记了上次对话的情况甚至已经忘记了以前是否对话过。所以,引入Session机制让Web服务器与一个客户端的交流过程更像是一个对话。