摘要:尽管session机制在web应用程序中被採用已经非常长时间了。可是仍然有非常多人不清楚session机制的本质,以至不能正确的应用这一技术。
本文将详细讨论session的工作机制而且对在Java web application中应用session机制时常见的问题作出解答。
文件夹:
一、术语session
二、HTTP协议与状态保持
三、理解cookie机制
四、理解session机制
五、理解javax.servlet.http.HttpSession
六、HttpSession常见问题
七、跨应用程序的session共享
八、总结
一、术语session
session,中文常常翻译为会话,其本来的含义是指有始有终的一系列动作/消息,比方打电话时从拿起电话拨号到挂断电话这中间的一系列过程能够称之为一个 session。
有时候我们能够看到这样的话“在一个浏览器会话期间,...”。这里的会话一词用的就是其本义,是指从一个浏览器窗体打开到关闭这个期间 。
最混乱的是“用户(client)在一次会话期间”这样一句话,它可能指用户的一系列动作(普通情况下是同某个详细目的相关的一系列动作。比方从登录到选购商品到结账登出这样一个网上购物的过程。有时候也被称为一个transaction),然而有时候也可能仅仅是指一次连接,也有可能是指含义,当中的差别仅仅能靠上下文来判断。
然而当session一词与网络协议相关联时,它又往往隐含了“面向连接”和/或“保持状态”这样两个含义。 “面向连接”指的是在通信两方在通信之前要先建立一个通信的渠道。比方打电话。直到对方接了电话通信才干開始。与此相对的是写信。在你把信发出去的时候你并不能确认对方的地址是否正确,通信渠道不一定能建立,但对发信人来说,通信已经開始了。
“保持状态”则是指通信的一方能够把一系列的消息关联起来,使得消息之间能够互相依赖。比方一个服务员能够认出再次光临的老顾客而且记得上次这个顾客还欠店里一块钱。这一类的样例有“一个TCP session”或者 “一个POP3 session”。
而到了webserver蓬勃发展的时代,session在web开发语境下的语义又有了新的扩展。它的含义是指一类用来在client与server之间保持状态的解决方式④。
有时候session也用来指这样的解决方式的存储结构,如“把xxx保存在session 里”。
因为各种用于web开发的语言在一定程度上都提供了对这样的解决方式的支持。所以在某种特定语言的语境下,session也被用来指代该语言的解决方式,比方常常把Java里提供的javax.servlet.http.HttpSession简称为session。
鉴于这样的混乱已不可改变,本文中session一词的运用也会依据上下文有不同的含义,请大家注意分辨。
在本文中,使用中文“浏览器会话期间”来表达含义。使用“session机制”来表达含义,使用“session”表达含义,使用详细的“HttpSession”来表达含义
二、HTTP协议与状态保持
但有时须要server保留client的信息,以识别同一个客户的多次訪问,那又怎样实现呢?
三、理解cookie机制
cookie机制的基本原理就如上面的样例一样简单,可是还有几个问题须要解决:“会员卡”怎样分发;“会员卡”的内容。以及客户怎样使用“会员卡”。
正统的cookie分发是通过扩展HTTP协议来实现的。server通过在HTTP的响应头中加上一行特殊的指示以提示浏览器依照指示生成对应的cookie。然而纯粹的client脚本如JavaScript或者VBScript也能够生成cookie。
而cookie 的使用是由浏览器依照一定的原则在后台自己主动发送给server的。浏览器检查全部存储的cookie,假设某个cookie所声明的作用范围大于等于将要请求的资源所在的位置,则把该cookie附在请求资源的HTTP请求头上发送给server。意思是麦当劳的会员卡仅仅能在麦当劳的店里出示,假设某家分店还发行了自己的会员卡,那么进这家店的时候除了要出示麦当劳的会员卡,还要出示这家店的会员卡。
cookie的内容主要包括:名字。值。过期时间。路径和域。当中域能够指定某一个域比方.google.com。相当于总店招牌。比方宝洁公司,也能够指定一个域下的详细某台机器比方www.google.com或者froogle.google.com,能够用飘柔来做比。路径就是跟在域名后面的URL路径,比方/或者/foo等等。能够用某飘柔专柜做比。
路径与域合在一起就构成了cookie的作用范围。
假设不设置过期时间,则表示这个cookie的生命期为浏览器会话期间,仅仅要关闭浏览器窗体,cookie就消失了。
这样的生命期为浏览器会话期的 cookie被称为会话cookie。会话cookie一般不存储在硬盘上而是保存在内存里,当然这样的行为并非规范规定的。假设设置了过期时间,浏览器就会把cookie保存到硬盘上,关闭后再次打开浏览器,这些cookie仍然有效直到超过设定的过期时间。
存储在硬盘上的cookie 能够在不同的浏览器进程间共享,比方两个IE窗体。而对于保存在内存里的cookie。不同的浏览器有不同的处理方式。
对于IE。在一个打开的窗体上按 Ctrl-N(或者从文件菜单)打开的窗体能够与原窗体共享,而使用其它方式新开的IE进程则不能共享已经打开的窗体的内存cookie;对于 Mozilla Firefox0.8。全部的进程和标签页都能够共享相同的cookie。一般来说是用javascript的window.open打开的窗体会与原窗体共享内存cookie。
浏览器对于会话cookie的这样的仅仅认cookie不认人的处理方式常常给採用session机制的web应用程序开发人员造成非常大的困扰。
四、理解session机制
session机制是一种server端的机制。server使用一种相似于散列表的结构(也可能就是使用散列表)来保存信息。
当程序须要为某个client的请求创建一个session的时候,server首先检查这个client的请求里是否已包括了一个session标识 - 称为 session id,假设已包括一个session id则说明曾经已经为此client创建过session,server就依照session id把这个 session检索出来使用(假设检索不到,可能会新建一个),假设client请求不包括session id,则为此client创建一个session而且生成一个与此session相关联的session id,session id的值应该是一个既不会反复,又不easy被找到规律以仿造的字符串。这个 session id将被在本次响应中返回给client保存。
保 存这个session id的方式能够採用cookie,这样在交互过程中浏览器能够自己主动的依照规则把这个标识发挥给server。
一般这个cookie的名字都是相似于SEEESIONID,而。比方weblogic对于web应用程序生成的cookie,JSESSIONID= ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764,它的名字就是 JSESSIONID。
因为cookie能够被人为的禁止,必须有其它机制以便在cookie被禁止时仍然能够把session id传递回server。常常被使用的一种技术叫做URL重写。就是把session id直接附加在URL路径的后面,附加方式也有两种。一种是作为URL路径的附加信息,表现形式为http://...../xxx;jsessionid= ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764 jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764">http://...../xxx? jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764
还有一种是作为查询字符串附加在URL后面,表现形式为
这两种方式对于用户来说是没有差别的,仅仅是server在解析的时候处理的方式不同。採用第一种方式也有利于把session id的信息和正常程序參数区分开来。
为了在整个交互过程中始终保持状态,就必须在每一个client可能请求的路径后面都包括这个session id。
还有一种技术叫做表单隐藏字段。就是server会自己主动改动表单,加入一个隐藏字段。以便在表单提交时能够把session id传递回server。比方以下的表单
<form name="testform" action="/xxx">
<input type="text">
</form>
在被传递给client之前将被改写成
<form name="testform" action="/xxx">
<input type="hidden" name="jsessionid" value="ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764">
<input type="text">
</form>
这样的技术如今已较少应用,笔者接触过的非常古老的iPlanet6(SunONE应用server的前身)就使用了这样的技术。
实际上这样的技术能够简单的用对action应用URL重写来取代。
在谈论session机制的时候,常常听到这样一种误解“仅仅要关闭浏览器,session就消失了”。事实上能够想象一下会员卡的样例,除非顾客主动对店家提出销卡。否则店家绝对不会轻易删除顾客的资料。对session来说也是一样的。除非程序通知server删除一个session。否则server会一直保留,程序一般都是在用户做log off的时候发个指令去删除session。
然而浏览器从来不会主动在关闭之前通知server它将要关闭,因此server根本不会有机会知道浏览器已经关闭,之所以会有这样的错觉,是大部分session机制都使用会话cookie来保存session id,而关闭浏览器后这个 session id就消失了,再次连接server时也就无法找到原来的session。假设server设置的cookie被保存到硬盘上,或者使用某种手段改写浏览器发出的HTTP请求头,把原来的session id发送给server,则再次打开浏览器仍然能够找到原来的session。
恰恰是因为关闭浏览器不会导致session被删除,迫使server为seesion设置了一个失效时间,当距离client上一次使用session的时间超过这个失效时间时。server就能够觉得client已经停止了活动,才会把session删除以节省存储空间。
五、理解javax.servlet.http.HttpSession
HttpSession是Java平台对session机制的实现规范。因为它仅仅是个接口,详细到每一个web应用server的提供商,除了对规范支持之外,仍然会有一些规范里没有规定的细微差异。
这里我们以BEA的Weblogic Server8.1作为样例来演示。
首先。Weblogic Server提供了一系列的參数来控制它的HttpSession的实现,包括使用cookie的开关选项,使用URL重写的开关选项,session持久化的设置。session失效时间的设置。以及针对cookie的各种设置,比方设置cookie的名字、路径、域, cookie的生存时间等。
普通情况下,session都是存储在内存里。当server进程被停止或者重新启动的时候。内存里的session也会被清空,假设设置了session的持久化特性,server就会把session保存到硬盘上,当server进程又一次启动或这些信息将能够被再次使用。 Weblogic Server支持的持久性方式包括文件、数据库、clientcookie保存和复制。
复制严格说来不算持久化保存,因为session实际上还是保存在内存里,只是相同的信息被拷贝到各个cluster内的server进程中,这样即使某个server进程停止工作也仍然能够从其它进程中取得session。
cookie生存时间的设置则会影响浏览器生成的cookie是否是一个会话cookie。
默认是使用会话cookie。有兴趣的能够用它来试验我们在第四节里提到的那个误解。
cookie的路径对于web应用程序来说是一个非常重要的选项,Weblogic Server对这个选项的默认处理方式使得它与其它server有明显的差别。
后面我们会专题讨论。
六、HttpSession常见问题
1、session在何时被创建
一个常见的误解是以为session在有client訪问时就被创建,然而事实是直到某server端程序调用 HttpServletRequest.getSession(true)这样的语句时才被创建,注意假设JSP没有显示的使用 <% @page session="false"%> 关闭session。则JSP文件在编译成Servlet时将会自己主动加上这样一条语句 HttpSession session = HttpServletRequest.getSession(true);这也是JSP中隐含的 session对象的来历。
因为session会消耗内存资源,因此,假设不打算使用session,应该在全部的JSP中关闭它。
2、session何时被删除
综合前面的讨论,session在下列情况下被删除a.程序调用HttpSession.invalidate();或b.距离上一次收到client发送的session id时间间隔超过了session的超时设置;或c.server进程被停止(非持久session)
3、怎样做到在浏览器关闭时删除session
严格的讲,做不到这一点。能够做一点努力的办法是在全部的client页面里使用javascript代码window.oncolose来监视浏览器的关闭动作,然后向server发送一个请求来删除session。可是对于浏览器崩溃或者强行杀死进程这些非常规手段仍然无能为力。
4、有个HttpSessionListener是怎么回事
你能够创建这样的listener去监控session的创建和销毁事件,使得在发生这样的事件时你能够做一些对应的工作。
注意是session的创建和销毁动作触发listener。而不是相反。相似的与HttpSession有关的listener还有 HttpSessionBindingListener,HttpSessionActivationListener和 HttpSessionAttributeListener。
5、存放在session中的对象必须是可序列化的吗
没必要的。要求对象可序列化仅仅是为了session能够在集群中被复制或者能够持久保存或者在必要时server能够临时把session交换出内存。在 Weblogic Server的session中放置一个不可序列化的对象在控制台上会收到一个警告。我所用过的某个iPlanet版本号假设 session中有不可序列化的对象,在session销毁时会有一个Exception,非常奇怪。
6、怎样才干正确的应付client禁止cookie的可能性
对全部的URL使用URL重写,包括超链接,form的action,和重定向的URL,详细做法參见
7、开两个浏览器窗体訪问应用程序会使用同一个session还是不同的session
參见第三小节对cookie的讨论。对session来说是仅仅认id不认人,因此不同的浏览器,不同的窗体打开方式以及不同的cookie存储方式都会对这个问题的答案有影响。
8、怎样防止用户打开两个浏览器窗体操作导致的session混乱
这个问题与防止表单多次提交是相似的,能够通过设置client的令牌来解决。就是在server每次生成一个不同的id返回给client,同一时候保存在session里。client提交表单时必须把这个id也返回server,程序首先比較返回的id与保存在session里的值是否一致。假设不一致则说明本次操作已经被提交过了。能够參看《J2EE核心模式》关于表示层模式的部分。
须要注意的是对于使用javascript window.open打开的窗体。一般不设置这个id。或者使用单独的id,以防主窗体无法操作,建议不要再window.open打开的窗体里做改动操作,这样就能够不用设置。
9、为什么在Weblogic Server中改变session的值后要又一次调用一次session.setValue
做这个动作主要是为了在集群环境中提示Weblogic Server session中的值发生了改变。须要向其它server进程复制新的session值。
10、为什么session不见了
排除session正常失效的因素之外,server本身的可能性应该是微乎其微的。尽管笔者在iPlanet6SP1加若干补丁的Solaris版本号上倒也遇到过;浏览器插件的可能性次之,笔者也遇到过3721插件造成的问题;理论上防火墙或者代理server在cookie处理上也有可能会出现故障。
出现这一问题的大部分原因都是程序的错误,最常见的就是在一个应用程序中去訪问另外一个应用程序。
我们在下一节讨论这个问题。
七、跨应用程序的session共享
常常有这样的情况,一个大项目被切割成若干小项目开发,为了能够互不干扰,要求每一个小项目作为一个单独的web应用程序开发,可是到了最后突然发现某几个小项目之间须要共享一些信息,或者想使用session来实现SSO(single sign on),在session中保存login的用户信息,最自然的要求是应用程序间能够訪问彼此的session。
然而依照Servlet规范。session的作用范围应该仅仅限于当前应用程序下,不同的应用程序之间是不能够互相訪问对方的session的。各个应用server从实际效果上都遵守了这一规范。可是实现的细节却可能各有不同。因此解决跨应用程序session共享的方法也各不相同。
首先来看一下Tomcat是怎样实现web应用程序之间session的隔离的。从 Tomcat设置的cookie路径来看,它对不同的应用程序设置的cookie路径是不同的,这样不同的应用程序所用的session id是不同的,因此即使在同一个浏览器窗体里訪问不同的应用程序。发送给server的session id也能够是不同的。
八、总结
session机制本身并不复杂,然而事实上现和配置上的灵活性却使得详细情况复杂多变。
这也要求我们不能把仅仅某一次的经验或者某一个浏览器,server的经验当作普遍适用的经验。而是始终须要详细情况详细分析。
摘要:尽管session机制在web应用程序中被採用已经非常长时间了。可是仍然有非常多人不清楚session机制的本质。以至不能正确的应用这一技术。本文将详细讨论session的工作机制而且对在Java web application中应用session机制时常见的问题作出解答。