java Web开发——Session和Cookie的深入研究

来源:互联网 发布:sql数据库数据类型 编辑:程序博客网 时间:2024/06/06 18:37

Session是一种服务器端技术,Session对象在服务器端创建,通常采用散列表来存储信息,例如,TomcatSession实现采用HashMap对象来存储属性名和属性值。

Cookie是由Netscape公司发明的、用于跟踪用户会话的一种方式。Cookie是由服务器发送给客户的片段信息,存储在客户端浏览器的内存中或硬盘上,在客户随后对该服务器的请求中发回它。

SessionCookie的最大的区别是,Session在服务端保存信息,Cookie在客户端保存信息。为了跟踪用户的会话,服务器端在创建Session后,需要将Session ID交给客户端,在客户端下次请求时,将这个ID随请求一起发送回来。可以采用CookieURL重写的方式,将Session ID发送给客户端。

基于Cookie的会话跟踪的例子中,当访问了Servlet后,关闭浏览器,再次打开浏览器访问Servlet时,可以看到开始了一次新的会话。如果我们同时打开两个浏览器,访问同一个URL,那么每一个浏览器进程都将开始一个新的会话。而在“使用Cookie的实例”中,当我们登录后,关闭浏览器,再打开浏览器,访问GreetServlet2时,直接出现了欢迎页面。然后我们同时打开两个浏览器,访问GreetServlet2时,也直接出现了欢迎页面。也就说,在例子中,保存Session IDCookie在关闭浏览器后就删除了,不能在多个浏览器进程间共享。而在“使用Cookie的实例”的例子中,保存用户名和密码的Cookie在浏览器关闭后,再次打开,仍然存在,可以在多个浏览器进程间共享。

通常,我们将用于会话跟踪的Cookie叫做会话CookieServlet规范中,用于会话跟踪的Cookie的名字必须是JSESSIONID,它通常保存在浏览器的内存中。在浏览器内存中的会话Cookie不能被不同的浏览器进程所共享。在网络上,很多人有一种错误的认识,认为以不同的方式打开浏览器窗口,或者使用其他的非IE浏览器就可以在不同的浏览器进程之间共享会话Cookie下面,我们和读者一起看看这种错误认识是如何产生的。

首先看看IE浏览器,启动Tomcat服务器,打开IE浏览器,访问第5.2.2节中的例子程序,在地址栏中输入:http://localhost:8080/ch05/login,然后单击IE浏览器菜单栏上的“文件”“新建”“窗口”(也可以在浏览器窗口上按Ctrl+N组合键)打开一个新窗口,分别刷新原窗口和新窗口,可以看到两个窗口中显示的Session ID是同一个。由实验可以看到,某些方式下打开的多个IE浏览器,可以共享内存中的Cookie,而另外一些方式打开的多个IE浏览器,则不能共享内存中的Cookie。再使用其他浏览器进行验证,发现有的浏览器(例如Mozilla FireFox)不管以什么方式打开窗口,都可以共享内存中的Cookie。由此,一个错误的结论就产生了:不同的浏览器对内存中的Cookie有不同的处理方式,有的浏览器(FireFox)可以在多个浏览器进程间共享会话CookieIE浏览器是否共享Cookie,要看浏览器打开的方式。

笔者为什么会说上述结论是错误的呢?这是因为得出结论的人不了解进程的概念。读者在以上述方式打开两个IE浏览器窗口后(如果有其他的IE浏览器打开,请先关闭),同时按下Ctrl+Alt+Del组合键,选择“任务管理器”,切换到“进程”标签页,在“映像名称”下查找IEXPLORE.EXE,看看找到了几个IEXPLORE.EXE,是不是只有一个!那么这说明了什么?说明我们所看到的两个IE浏览器窗口实际上是属于同一个IE浏览器进程,难怪它们能共享内存中的会话Cookie。以其他方式打开一个IE浏览器,可以看到在“映像名称”下多了一个IEXPLORE.EXE进程,既然是两个IE浏览器进程的窗口,当然无法共享内存中的Cookie了。同样的道理,Mozilla FireFox不管以什么方式打开窗口,始终只启动了一个进程,既然这些窗口同属于一个进程,当然可以共享内存中的Cookie了。所以我们说,对于存储在内存中的Cookie,是不能被不同的浏览器进程所共享的。共享只能发生在同一个浏览器进程的不同窗口中(因为这些窗口共享同一个进程地址空间)。关于进程和线程的知识,读者可以参看相关的书籍。

对于保存在硬盘上的Cookie,因为是在外部的存储设备中存储,所以可以在多个浏览器进程间共享。

有些人对Session的使用存在着一种误解,认为浏览器一旦关闭,Session就消失了。这是因为有的人看到,关闭浏览器后,再打开一个浏览器,就开始了一次新的会话,从而得出了结论。再回头看看第5.1节中的顾客在超市存包的例子,顾客存好包,购物完毕,忘了取包就走了,但存包处的管理员不知道顾客已经走了,所以他必须继续用柜子(相当于Session)存放顾客的物品,直到长时间没有人来取(Session的超时值发生),管理员才清除柜子。之所以会有“浏览器一旦关闭,Session就消失了”这种错误的认识,主要是因为保存Session IDCookie是存储在浏览器的内存中,一旦浏览器关闭,Cookie将被删除,Session ID也就丢失
了。当再次打开浏览器连接服务器时,服务器没有收到
Session ID,当然也就无法找到先前的Session,于是服务器就创建了一个新的Session。而这个时候先前的Session是仍然存在的,直到设置的Session超时时间间隔发生,Session才被服务器清除。如果我们将会话Cookie保存到硬盘上,或者通过某种技术手段改写浏览器向服务器发送的请求报头,将原先的Session ID发送给服务器,则再次打开的浏览器就能够找到原来的Session了。

我们想了解一下,用于会话跟踪的Cookie是如何创建的,为何只能保存在浏览器的内存中,而不能保存到用户的硬盘上。在Tomcat 6.0.16.中,Session的创建是调用org.apache.catalina.connector.Request类中的doGetSession()方法来完成的。下面我们给出这个方法的代码片段:

[java] view plaincopyprint?
  1. protected Session doGetSession(boolean create)  
  2.   
  3. {  
  4.   
  5.    …  
  6.   
  7.    // Creating a new session cookie based on that session  
  8.   
  9.    if ((session != null) && (getContext() != null)  
  10.   
  11.           && getContext().getCookies())  
  12.   
  13.    {  
  14.   
  15.        Cookie cookie = new Cookie(Globals.SESSION_COOKIE_NAME,  
  16.   
  17.                                           session.getIdInternal());  
  18.   
  19.         configureSessionCookie(cookie);  
  20.   
  21.         response.addCookieInternal(cookie);  
  22.   
  23.     }  
  24.   
  25.   
  26.   
  27.     if (session != null)  
  28.   
  29.     {  
  30.   
  31.         session.access();  
  32.   
  33.         return (session);  
  34.   
  35.     }  
  36.   
  37.     else  
  38.   
  39.     {  
  40.   
  41.         return (null);  
  42.   
  43.     }  
  44.   
  45. }  
  46.   
  47.   
  48.   
  49. protected void configureSessionCookie(Cookie cookie)  
  50.   
  51. {  
  52.   
  53.     cookie.setMaxAge(-1);  
  54.   
  55.     String contextPath = null;  
  56.   
  57.     if (!connector.getEmptySessionPath() && (getContext() != null))  
  58.   
  59.     {  
  60.   
  61.         contextPath = getContext().getEncodedPath();  
  62.   
  63.     }  
  64.   
  65.     if ((contextPath != null) && (contextPath.length() > 0))  
  66.   
  67.     {  
  68.   
  69.         cookie.setPath(contextPath);  
  70.   
  71.     }  
  72.   
  73.     else  
  74.   
  75.     {  
  76.   
  77.         cookie.setPath("/");  
  78.   
  79.     }  
  80.   
  81.     if (isSecure())  
  82.   
  83.     {  
  84.   
  85.         cookie.setSecure(true);  
  86.   
  87.     }  
  88.   
  89. }  

代码的第8行,我们看到非常熟悉的创建Cookie对象的代码,Cookie的名字是Globals.SESSION_COOKIE_NAMESESSION_COOKIE_NAME被定义为静态的常量,其值为JSESSIONIDCookie的值是调用session.getIdInternal()得到的Session ID。第10行,调用了configureSessionCookie()方法来配置会话Cookie。我们转到configureSessionCookie()方法中,第27行,调用Cookie对象的setMaxAge()方法设置Cookie的生存时间,在“使用Cookie的实例”的例子中,我们说过,如果时间值为负数,那么当客户端的浏览器退出,Cookie将会被删除。看到这儿,我们就知道了为什么会话Cookie只能保存在内存中了,这是由Tomcat的实现决定的。第35行,调用Cookie对象的setPath()方法,指定这个Cookie在当前Web应用程序的上下文路径下有效
原创粉丝点击