`

解析Servlet/JSP会话跟踪机制

 
阅读更多

在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> 

分享到:
评论

相关推荐

    JAVA WEB 开发详解:XML+XSLT+SERVLET+JSP 深入剖析与实例应用.part2

    9.2 java servlet api的会话跟踪 306 9.2.1 httpsession接口 306 9.2.2 session的生命周期 307 9.2.3 cookie的应用 319 9.2.4 httpsessionbindinglistener接口 328 9.2.5 在线人数统计程序 329 9.3 小结 335 ...

    JAVA WEB 开发详解:XML+XSLT+SERVLET+JSP 深入剖析与实例应用.part3

    9.2 java servlet api的会话跟踪 306 9.2.1 httpsession接口 306 9.2.2 session的生命周期 307 9.2.3 cookie的应用 319 9.2.4 httpsessionbindinglistener接口 328 9.2.5 在线人数统计程序 329 9.3 小结 335 ...

    JAVA WEB 开发详解:XML+XSLT+SERVLET+JSP 深入剖析与实例应用.part4

    9.2 java servlet api的会话跟踪 306 9.2.1 httpsession接口 306 9.2.2 session的生命周期 307 9.2.3 cookie的应用 319 9.2.4 httpsessionbindinglistener接口 328 9.2.5 在线人数统计程序 329 9.3 小结 335 ...

    JAVA WEB 开发详解:XML+XSLT+SERVLET+JSP 深入剖析与实例应用.part5

    9.2 java servlet api的会话跟踪 306 9.2.1 httpsession接口 306 9.2.2 session的生命周期 307 9.2.3 cookie的应用 319 9.2.4 httpsessionbindinglistener接口 328 9.2.5 在线人数统计程序 329 9.3 小结 335 ...

    java-servlet-api.doc

    JavaServletAPI提供了一个简单的接口,通过这个接口,Servlet引擎可以有效地跟踪用户的会话。 建立Session 因为HTTP是一个请求-响应协议,一个会话在客户机加入之前会被认为是一个新的会话。加入的意思是返回会话...

    JSP 程序设计从入门到精通 PDF 教程

     4.4.1 会话状态跟踪API 75  4.4.2 在会话对象中保存数据 76  4.4.3实例:显示会话信息 76  4.5 用Java Servlets代替CGI 78  4.6 JSP/Servlet 中的汉字编码问题 80  4.7 图解Eclipse+Tomcat集成开发...

    J2EE应用开发详解

    57 4.3.7 Servlet协作 57 4.3.8 Filter 58 4.4 使用Servlet处理客户端请求 58 4.5 会话跟踪 61 4.5.1 使用Cookie进行会话跟踪 61 4.5.2 使用URL重写进行会话跟踪 62 4.5.3 使用隐藏表单域进行会话跟踪 63 4.6 小结 ...

    千方百计笔试题大全

    181、四种会话跟踪技术 42 182、Request对象的主要方法 43 183、我们在web应用开发过程中经常遇到输出某种编码的字符,如iso8859-1等,如何输出一个某种编码的字符串? 43 184、Servlet执行时一般实现哪几个方法? ...

    javaweb详细笔记,绝对详细(内含代码)

    Xml文档及解析 31 反射 33 Http协议和Tomcat服务器 36 JavaWeb核心之Servlet 41 HttpServletResponse 46 HttpServletRequest 51 会话技术Cookie&Session; 55 动态页面技术(JSP/EL/JSTL) 59 javaEE的开发模式 65 ...

    java面试宝典

    181、四种会话跟踪技术 42 182、Request对象的主要方法 43 183、我们在web应用开发过程中经常遇到输出某种编码的字符,如iso8859-1等,如何输出一个某种编码的字符串? 43 184、Servlet执行时一般实现哪几个方法? ...

    Java Web编程宝典-十年典藏版.pdf.part2(共2个)

    5.3.4 Servlet配置解析 5.3.5 使用Servlet处理业务逻辑 5.3.6 掌握Servelet处理流程 5.3.7 Eclipse快速创建Servlet 5.4 一夫当关——配置并应用Servlet过滤器 5.4.1 过滤器的基本原理 5.4.2 过滤器的核心API 5.4.3 ...

    Java-Web程序设计(PPT).pptx

    课程概述 终点 起点 1 2 3 4 5 6 7 1.Web应用程序基础 2.JSP基础 3.JSP内置对象 5. Servlet技术 4.JavaBean和标准动作 6. Servlet技术进阶 8. 会话跟踪技术进阶 7. 会话跟踪技术 8 9 9.EL表达式 10 10.JSTL标签库 ...

    java 面试题 总结

    assertion(断言)在软件开发中是一种常用的调试方式,很多开发语言中都支持这种机制。在实现中,assertion就是在程序中的一条语句,它对一个boolean表达式进行检查,一个正确程序必须保证这个boolean表达式的值为...

    JAVA编程百例(照着例子更容易!)

    实例80 使用SERVLET获取客户表单 实例81 SERVLET读取COOKIE数据 实例82 SERVLET读取SESSION数据 实例83 一个简单的JSP 实例84 JSP处理表单提交 实例85 JSP连接数据库 实例86 JSP使用数据连接池 实例87 JSP+BEAN的...

    超级有影响力霸气的Java面试题大全文档

    assertion(断言)在软件开发中是一种常用的调试方式,很多开发语言中都支持这种机制。在实现中,assertion就是在程序中的一条语句,它对一个boolean表达式进行检查,一个正确程序必须保证这个boolean表达式的值为...

    Java及大数据学习路线.pdf

    1.Java学习路线 1.1JavaSE Java基础 Java8新特性 数据库MySQL与SQL语⾔ JDBC 1.2JavaWeb HTML基础与CSS JavaScript DOM 与 jQuery XML与Tomcat HTTP协议 服务器端组件Servlet JSP EL表达式 JSTL 会话控制Cookie和...

    java基础题 很全面

    19. 四种会话跟踪技术 18 20. J2EE是什么? 19 21. J2EE是技术还是平台还是框架? 19 22. 请对以下在J2EE中常用的名词进行解释(或简单描述) 19 23. WEB SERVICE名词解释。JSWDL开发包的介绍。JAXP、JAXM的解释。SOAP、...

    java面试题

    28. JSP和Servlet有哪些相同点和不同点,他们之间的联系是什么? 15 29. Anonymous Inner Class (匿名内部类) 是否可以extends(继承)其它类,是否可以implements(实现)interface(接口)? 15 30. 构造器Constructor...

    EL表达式 (详解)

    sessionScope表示会话 范围内的变量.applicationScope表示应用范围的变量. 3 --&lt;%@ page isELIgnored="true"%&gt; 表示是否禁用EL语言,TRUE表示禁止.FALSE表示不禁 止.JSP2.0中默认的启用EL语言. 4-- EL语言可...

Global site tag (gtag.js) - Google Analytics