在Web服务器端编程中,会话状态管理是一个经常必须考虑的重要问题。本文分析JSP/Servlet的会话管理机制及其所面临的问题,然后提出了一种改进的会话管理方法。

一、Servlet的会话管理机制

根据设计,HTTP是一种无状态的协议。它意味着Web应用并不了解有关同一用户以前请求的信息。维持会话状态信息的方法之一是使用Servlet或者JSP容器提供的会话跟踪功能。Servlet API规范定义了一个简单的HttpSession接口,通过它我们可以方便地实现会话跟踪。

HttpSession接口提供了存储和返回标准会话属性的方法。标准会话属性如会话标识符、应用数据等,都以“名字-值”对的形式保存。简而言之,HttpSession接口提供了一种把对象保存到内存、在同一用户的后继请求中提取这些对象的标准办法。在会话中保存数据的方法是setAttribute(String s, Object o),从会话提取原来所保存对象的方法是getAttribute(String s)。

在HTTP协议中,当用户不再活动时不存在显式的终止信号。由于这个原因,我们不知道用户是否还要再次返回,如果不采取某种方法解决这个问题,内存中会积累起大量的HttpSession对象。

为此,Servlet采用“超时限制”的办法来判断用户是否还在访问:如果某个用户在一定的时间之内没有发出后继请求,则该用户的会话被作废,他的HttpSession对象被释放。会话的默认超时间隔由Servlet容器定义。这个值可以通过getMaxInactiveInterval方法获得,通过setMaxInactiveInterval方法修改,这些方法中的超时时间以秒计。如果会话的超时时间值设置成-1,则会话永不超时。Servlet可以通过getLastAccessedTime方法获得当前请求之前的最后一次访问时间。

要获得HttpSession对象,我们可以调用HttpServletRequest对象的getSession方法。为了正确地维持会话状态,我们必须在发送任何应答内容之前调用getSession方法。

用户会话既可以用手工方法作废,也可以自动作废。作废会话意味着从内存中删除HttpSession对象以及它的数据。例如,如果一定时间之内(默认30分钟)用户不再发送请求,Java Web Server自动地作废他的会话。

Servlet/JSP会话跟踪机制有着一定的局限,比如:

会话对象保存在内存之中,占用了可观的资源。

会话跟踪依赖于Cookie。由于各种原因,特别是安全上的原因,一些用户关闭了Cookie。

会话跟踪要用到服务器创建的会话标识符。在多个Web服务器以及多个JVM的环境中,Web服务器不能识别其他服务器创建的会话标识符,会话跟踪机制无法发挥作用。

要深入理解会话跟踪机制,首先我们必须理解在Servlet/JSP容器中会话如何运作。

二、会话标识符

每当新用户请求一个使用了HttpSession对象的JSP页面,JSP容器除了发回应答页面之外,它还要向浏览器发送一个特殊的数字。这个特殊的数字称为“会话标识符”,它是一个唯一的用户标识符。此后,HttpSession对象就驻留在内存之中,等待同一用户返回时再次调用它的方法。

在客户端,浏览器保存会话标识符,并在每一个后继请求中把这个会话标识符发送给服务器。会话标识符告诉JSP容器当前请求不是用户发出的第一个请求,服务器以前已经为该用户创建了HttpSession对象。此时,JSP容器不再为用户创建新的HttpSession对象,而是寻找具有相同会话标识符的HttpSession对象,然后建立该HttpSession对象和当前请求的关联。

会话标识符以Cookie的形式在服务器和浏览器之间传送。如果浏览器不支持Cookie又如何呢此时,对服务器的后继请求将不会带有会话标识符。结果,JSP容器认为该请求来自一个新用户,它会再创建一个HttpSession对象,而以前创建的HttpSession对象仍旧驻留在内存中,但该用户以前的会话信息却丢失了。

另外,Servlet/JSP容器只认可它自己创建的会话标识符。如果同一Web应用在“Web农场”(Web farm)的多台服务器上运行,则必须存在这样一种机制:保证来自同一用户的请求总是被定向到处理该用户第一次请求的服务器。

三、伪会话管理机制

如前所述,基于Cookie的会话管理技术面临着种种问题。下面我们要设计一种新的会话管理机制来解决这些问题。这种会话管理机制称为“伪会话”(Pseudo Session)机制,它具有如下特点:

对象和数据不是保存在内存中,而是以文本文件形式保存。每一个文本文件与一个特定的用户关联,文件的名字就是会话的标识符。因此,文件名字必须是唯一的。

文本文件保存在一个专用的目录中,所有Web服务器都可以访问这个目录。因此,伪会话可以用于Web农场。

会话标识符不作为Cookie发送,而是直接编码到URL里面。因此,采用伪会话技术要求修改所有的超级链接,包括HTML表单的ACTION属性。

此外,实现伪会话管理机制时我们还要考虑到以下几点:

它应该与应用无关,其他想要实现同样功能的开发者应该能够方便地重用它。

考虑到安全原因,应该有一种为会话标识符生成随机数字的办法。

为了作废过期的会话,应该设定一个超时值。同一个用户,如果他超过一定的时间之后再次返回,他将获得一个新的会话标识符。此举能够防止未经授权的用户冒用其他人的会话。

应该有一种收集过期会话并删除相应文本文件的机制。

如果用户使用已经过期的会话标识符再次访问服务器,即使这个会话标识符的文本文件还没有删除,系统也不应该允许用户使用原来的会话。

同时,应该存在一种更新会话文本文件最后改动时间的机制,使得用户在会话过期时限之前返回时会话总是保持最新且合法的状态数据
四、实现伪会话管理机制

下面所介绍的工程称为PseudoSession,它是伪会话机制一个很简单的实现。考虑到移植性,我们以JavaBean的形式实现它。PseudoSessionBean的完整代码可以从本文后面下载。

PseudoSessionBean拥有如下域(Field):

public String path;public long timeOut;

path是保存所有会话文本文件的目录。如果Web服务器的数量在一个以上,这个目录必须允许所有服务器访问。然而,为了防止用户直接访问这些文本文件,这个路径应该不允许用户直接访问。解决这个问题的一种方法是使用Web网站根之外的目录。

timeOut是用户的最后一个请求到会话过期作废之间的时间。在PseudoSessionBean的代码清单中,timeOut设置成了以毫秒表示的20分钟,这是一个比较合理的超时时间值。对于任何用户,如果他在这个超时时间之后才继续发出请求,他将得到一个新的会话标识符。

PseudoSessionBean有4个方法:getSessionID,setValue,getValue,deleteAllInvalidSessions。

4.1、getSessionID方法

getSessionID方法的声明如下:

public String getSessionID(HttpServletRequest request)

这个方法应该在每一个JSP页面的开头调用。它完成如下任务:

如果用户是第一次访问,则为该用户设定一个新的会话标识符。

检查URL所带会话标识符的合法性。如果会话标识符已经过期,则getSessionID方法返回一个新的会话标识符。

下面我们来看看getSessionID方法的工作过程。

String sessionId = request.getParameter("sessionId");

validSessionIdFound是一个标记,用于指示会话标识符是否合法。validSessionIdFound的初始值是false。

boolean validSessionIdFound = false;

long类型的now变量包含请求出现时的服务器时间。该变量用于确定用户会话的合法性。

long now = System.currentTimeMillis();

如果找到了会话标识符,则getSessionID方法检查它的合法性。检查过程如下:

一个合法的会话标识符必须有对应的同名文本文件。文件的最后修改时间加上timeOut应该大于当前时间。

如果存在与会话对应的文本文件,但文件已经过期,则原来的文件被删除。

把合法会话标识符所对应文本文件的最后修改日期改为now。

这些任务主要借助File对象完成,创建File对象的参数就是会话文本文件的路径:

if (sessionId!=null) {File f = new File(path + sessionId);if (f.exists()) {if (f.lastModified() + timeOut > now){// 会话合法// 使用setLastModified时,如果文件已经被其他程序锁定,// 程序不会产生任何异常,但文件数据不会改变f.setLastModified(now);validSessionIdFound = true;}else{// 会话已经过期// 删除文件f.delete(); }}//end if (f.exists) }//end if (sessionId!=null)

如果不存在合法的会话标识符,则getSessionID方法生成一个会话标识符以及相应的文本文件:

if (!validSessionIdFound) { sessionId = Long.toString(now); // 创建文件 File f = new File(path + sessionId); try {f.createNewFile(); }catch (IOException ioe) {}} // end of if !validSessionIdFound

程序保证文件名字随机性的方法非常简单:把当前的系统时间直接转换成会话标识符。对于那些涉及敏感数据的应用,我们应该考虑运用更安全的随机数生成器来生成会话标识符。

综上所述,getSessionID并不总是返回新的合法会话标识符:它返回的标识符可能与传递给它的标识符相同,也可能是新创建的会话标识符。

为了保证JSP页面拥有合法的会话标识符以便调用setValue、getValue方法,每个JSP页面都必须在开头位置调用getSesstionID方法
然后,我们用JSP的< jsp:useBean>标记告诉JSP容器程序要使用PseudoSessionBean:

< jsp:useBean id="PseudoSessionId" scope="application"class="pseudosession.PseudoSessionBean" />

在上面这个< jsp:useBean>标记中,class属性值是“包.类名字”形式。当然,对于不同的包名字,class属性的值应该作相应的修改。注意Bean的scope属性是“application”,这是因为我们要在应用的所有页面中使用这个Bean。在这个应用中,把Bean的scope属性设置为“application”具有最好的效率,因为我们只需创建Bean对象一次就可以了。另外,正如前面所提到的,getSessionID方法必须在所有其他代码之前调用。

< % String sessionId = PseudoSessionId.getSessionID(request);%>

为了说明PseudoSessionBean的应用,下面我们来看两个JSP页面,它们是index.jsp和secondPage.jsp。index.jsp页面在伪会话变量中保存用户的名字,而secondPage.jsp则提取这个用户名字。

index.jsp页面的代码如下:

< %@ page session="false" contentType="text/html;charset=gb2312" %>< jsp:useBean id="PseudoSessionId" scope="application"class="pseudosession.PseudoSessionBean" />< % String sessionId = PseudoSessionId.getSessionID(request);%>< html>< head>< title>伪会话< /title>< /head>< body>< h1>伪会话管理机制< /h1>< % String userName = "bulbul"; PseudoSessionId.setValue(sessionId, "userName", userName);%>< a href="/secondPage.jsp?sessionId=< ";%=sessionId%>>点击此处< /a>< form method="post" action=anotherPage.jsp?sessionId=< %=sessionId%>>输入数据:< input type="text" name="sample">< input type="submit" name="Submit" value="Submit">< /form>< /body>< /html>< % PseudoSessionId.deleteAllInvalidSessions();%>

注意,包括< form>标记的action属性在内,所有的超级链接都已经改写,现在都包含了会话标识符。另外也请注意页面的最后调用了deleteAllInvalidSessions方法。

secondPage.jsp页面只简单地返回以前保存的用户名字。

< %@ contentType="text/html;charset=gb2312" page session="false" %>< jsp:useBean id="PseudoSessionId" scope="application"class="pseudosession.PseudoSessionBean" />< % String sessionId = PseudoSessionId.getSessionID(request);%>< html>< head>< title>第2个页面< /title>< /head>< body>< % String userName = PseudoSessionId.getValue(sessionId,"userName");out.println("用户名字是 " + userName);%>< /body>< /html>

解析Servlet/JSP会话跟踪机制相关推荐

  1. 浏览器禁用Cookie,基于Cookie的会话跟踪机制失效的解决的方法

    当浏览器禁用Cookies时.基于Cookie的会话跟踪机制就会失效.解决的方法是利用URL重写机制跟踪用户会话. 在使用URL重写机制的时候须要注意.为了保证会话跟踪的正确性,全部的链接和重定向语句 ...

  2. WEB中会话跟踪[转]

    今天晚上去华工参加睿智融科的笔试,问到web会话跟踪,一脸懵比,这个词听都没听过,回来后百度下,发现其实会话跟踪的内容我基本都了解的~_~ 转自:http://www.cnblogs.com/gaop ...

  3. Servlet - 会话跟踪

    Servlet 标签 : Java与Web 会话跟踪 HTTP本身是"无状态"协议,它不保存连接交互信息,一次响应完成之后即连接断开,下一次请求需要重新建立连接,服务器不记录上次连 ...

  4. java web 请求跟踪_IT兄弟连 JavaWeb教程 Servlet会话跟踪 Cookie技术

    原标题:IT兄弟连 JavaWeb教程 Servlet会话跟踪 Cookie技术 Cookie使用HTTPHeader传递数据.Cookie机制定义了两种报头,Set-Cookie报头和Cookie报 ...

  5. 【Servlet】Session会话跟踪技术

    Session Session是指使用HttpSession对象实现会话跟踪的技术,是一种在服务器端保持会话跟踪的解决方案. HttpSession对象是javax.servlet.http.Http ...

  6. 【Servlet】Cookie会话跟踪技术

    Cookie Cookie技术是一种在客户端保持会话跟踪的解决方案,会话数据保存在客户端浏览器. Cookie在用户第一次访问服务器时,由服务器通过响应头的方式发送给客户端浏览器:当用户再次向服务器发 ...

  7. 2018.8.18 servlet使用的会话跟踪除session外还有哪些方式

    解释HTTP HTTP是一种无连接的协议,如果一个客户端只是单纯地请求一个文件(HTML或GIF),服务器端可以响应给客户端,并不需要知道一连串的请求是否来自于相同的客户端,而且也不需要担心客户端是否 ...

  8. Servlet+JSP一文完结

    目录 前言: 一.Servlet简介 二.生命周期 三.Servlet实例 四.Servlet表单数据 五.客户端HTTP请求. 六.Servlet服务器HTTP响应 七.Servlet HTTP状态 ...

  9. java跟踪会话_JavaWeb会话跟踪

    cookie和session是常用的会话跟踪技术 cookie机制 1.web应用程序是使用HTTP协议传输数据的,HTTP协议是无状态的协议,一旦数据交换完毕就会关闭链接.Cookie可以弥补HTT ...

最新文章

  1. 从tomcat下载文件的配置方法(很全呢)
  2. HDU 4406 最大费用最大流
  3. mqtt与硬件交互_一个关于小程序Iot的具体实现(MQTT版)
  4. 0459-Repeated Substring Pattern(重复的子字符串)
  5. 七夕-探探小卡片鸿蒙版
  6. Tesseract-OCR图片识别为文字
  7. 鸟叫就能黑掉AI系统,而且你根本察觉不到
  8. 不是区块链的特征_区块链的四大特征
  9. dynamic image
  10. 基于遥感影像实现三种方法提取枣树面积精度分析
  11. xshell 中使用vim 显示Xmanager运行失败:
  12. ZEMAX | 如何创建复杂的非序列物体
  13. wps右键失效_鼠标点击右键没有反应怎么办
  14. java繁体_Java-汉字繁体拼音转换
  15. 偷菜游戏,悄悄开启毁灭中国的魔盒
  16. 总有人问我:独立站该怎么玩?3个案例,你看完就懂了
  17. gb2312的字符串(包括中午)转16进制字符串以及反转义原始字符
  18. OSChina 周二乱弹 —— 同在一室不相亲,早晚相见不相识
  19. 人工智能案例集 | 连续值预测基础
  20. 推荐几种靠谱的离职原因回答策略

热门文章

  1. pip安装第三方库报错Retrying (Retry(total=1, connect=None, read=None, redirect=None, status=None))
  2. 莫名其妙的java.lang.NoClassDefFoundError
  3. 你不知道的Event Loop
  4. java正则匹配并提取字串
  5. 生成不同数据类型的随机数
  6. STL泛型编程之map映照容器
  7. SDL2.0文档翻译
  8. node.js中获取请求当前页的前一页URL地址
  9. 【ABAP系列】SAP ABAP 宏的简单使用
  10. 转载 树莓派vnc 教程