`
simonyq
  • 浏览: 23674 次
  • 性别: Icon_minigender_1
  • 来自: 重庆
社区版块
存档分类
最新评论

会话相关

阅读更多

在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方法。 

  4.2 setValue方法 

  setValue方法保存value字符串以及与它关联的字符串名字。这种“名字-值”对很容易使人想起Dictionary对象。setValue方法要求在第一个参数中提供合法的会话标识符,它假定在自己被调用之前getSessionID方法已经执行,经过检验的合法会话标识符必然存在,因此它不再对传入的会话标识符进行合法性检验。 

  setValue方法按如下规则保存名字-值对: 

  ? 如果与value值关联的name以前还没有保存过,则新的名字-值对加入到文本文件的末尾。 
  ? 如果value字符串关联的name值以前已经保存过,则原来保存的值被新的value值替换。 
  setValue方法按照如下格式保存名字-值对,注意“名字”是大小写敏感的:


  name-1 value-1name-2 value-2name-3 value-3...name-n value-n 

  setValue方法的声明如下: 

  public void setValue(String sessionId, String name, String value) 
  setValue方法首先寻找与当前会话对应的文本文件。如果不能找到文本文件,则setValue方法不做任何事情直接返回。如果找到了会话文本文件,setValue方法读取文本文件的各个行,然后比较读入的行与name:如果读入的文本行开头与name一样,则说明该名字已经保存,setValue方法将替换该行后面的值;如果name不能与读入的文本行匹配,则这行文本被直接复制到一个临时文件。 

  这部分功能的实现代码如下: 

try { FileReader fr = new FileReader(path + sessionId); BufferedReader br = new BufferedReader(fr); FileWriter fw = new FileWriter(path + sessionId + ".tmp"); BufferedWriter bw = new BufferedWriter(fw); String s; while ((s = br.readLine()) != null)if (!s.startsWith(name + " ")) { bw.write(s); bw.newLine();} bw.write(name + " " + value); bw.newLine(); bw.close(); br.close(); fw.close(); bw.close(); . . .}catch (FileNotFoundException e) {}catch (IOException e) { System.out.println(e.toString());}  

  原来文本文件中的所有行复制到临时文件之后,setValue方法删除原来的文本文件,然后把临时文件改成会话文本文件的名字: 

File f = new File(path + sessionId + ".tmp");File dest = new File(path + sessionId);dest.delete();f.renameTo(dest);  

  4.3 getValue方法 

  getValue方法用于提取原来保存在伪会话中的数据。正如setValue方法,getValue方法也要求传入一个合法的会话标识符,而且getValue方法不再对传入的会话标识符进行合法性检查。getValue方法的第二个参数是待提取数据的name,返回值是与指定name关联的value。 

  getValue方法的声明如下: 

  public String getValue(String sessionId, String name)  

  getValue方法的基本执行过程如下:首先找到会话文本文件,然后按行读入直至找到与name匹配的文本行;找到匹配的文本行之后,getValue方法返回该行保存的value;如果不能找到,getValue方法返回null。 

  4.4 deleteAllInvalidSessions方法 

  deleteAllInvalidSessions方法删除那些与已经过期的会话关联的文本文件。由于调用getSessionID方法时过期的会话文本文件会被删除,deleteAllInvalidSessions方法并不是关键的方法。什么时候调用这个方法由应用自己决定。例如,我们可以编写一个专用的后台程序,由这个程序每天一次清除所有过期的文本文件。最简单的办法是在JSP文件末尾调用deleteAllInvalidSessions方法,但如果网站比较繁忙,重复地调用deleteAllInvalidSessions方法将降低整个网站的响应能力。一种明智的做法是:编写一个在访问量较少的时候自动进行清理的后台程序。 

  deleteAllInvalidSessions方法的声明如下: 

  public void deleteAllInvalidSessions()  

  它首先把所有会话文本文件的名字读入files字符串数组: 

  File dir = new File(path); String[] files = dir.list();  

  deleteAllInvalidSessions方法比较文本文件的最后修改时间(加上超时时间)和系统当前时间,确定会话是否过期。long类型的变量now用于保存系统的当前时间。 

long now = System.currentTimeMillis();  

  接下来,deleteAllInvalidSessions方法通过循环访问files数组,依次检查每个文件的lastModified属性。所有与过期会话关联的文件都将被删除:

for (int i=0; i<files.length; i++) { File f = new File(path + files[i]); if (f.lastModified() + timeOut > now) f.delete(); // 删除过期的会话文本文件}  
五、应用实例 
  编译好PseudoSessionBean这个JavaBean之后,我们就可以利用伪会话管理机制来管理Web应用的会话状态信息了。由于不必再使用服务器的会话管理机制,我们可以在page指令中把session属性设置为false关闭默认的JSP/Servlet会话管理功能。 

<%@ page session="false" %> 
然后,我们用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><br /><% String userName = "bulbul"; PseudoSessionId.setValue(sessionId, "userName", userName);%><a href=secondPage.jsp?sessionId=<%=sessionId%>>点击此处</a><br /><form method="post" action=anotherPage.jsp?sessionId=<%=sessionId%>><br />输入数据:<input type="text" name="sample"><br /><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> 

分享到:
评论

相关推荐

    Outlook打开会话相关的所有邮件

    标题中的“Outlook打开会话相关的所有邮件”指的是在Microsoft Outlook中查看同一会话或对话线程中的所有邮件。在Outlook中,一封邮件可能属于一个会话,这个会话包含了与该邮件主题相关的所有来往邮件。用户通常...

    用户登录,会话跟踪

    1. **Cookie**:Cookie是服务器发送到用户浏览器并存储在本地的一小块数据,它包含了与用户会话相关的信息。当用户再次向服务器发送请求时,浏览器会自动将该Cookie一起发送,服务器通过解析Cookie来识别用户。...

    Shiro 身份验证、授权、密码和会话管理

    Shiro 提供了会话监听器、超时控制、会话数据持久化等机制,方便开发者处理会话相关的业务逻辑。 5. **Web 支持**: Shiro 提供了内置的 Servlet Filter,如 `FormAuthenticationFilter` 和 `AuthorizationFilter`...

    python调用会话存档c语言sdk获取会话信息

    先前的python调用会话存档的demo示例过少,由此自己写了一个demo示例,示例只需将会话相关信息填入示例的相应位置即可拉取信息。(注:该示例调用的是Linux环境下的c语言sdk,整个流程已经调通,如有问题可以私信...

    试会话终止.docx

    理想情况下,退出登录后,所有与会话相关的数据,包括令牌,都应该被清除,以防止未经授权的访问。 5. **防御措施**:为防止上述问题,开发者应确保服务器端实施严格的会话管理策略,包括及时注销过期的会话和令牌...

    JS-BASICS:从会话9开始的与Javascript会话相关的任务

    "JS-BASICS:从会话9开始的与Javascript会话相关的任务"这个标题暗示我们将探讨一系列进阶的JavaScript概念,这些概念可能包括函数、对象、数组、DOM操作、事件处理等。 首先,我们来回顾一下JavaScript的基础。...

    【Android】短信应用——短信(会话)实时删除

    若要删除整个会话,需要先获取到会话ID(`thread_id`),然后删除与该会话相关的所有短信: ```java // 获取会话ID long threadId = ...; // 可能通过查询获取 // 删除会话中的所有短信 contentResolver.delete(Uri...

    电信设备-通信会话的辅助信息查询方法及装置.zip

    总结起来,"通信会话的辅助信息查询方法及装置"涉及到的是电信领域中对通信会话相关数据的高效检索与利用,这对于提升通信服务质量、优化网络性能以及保障用户隐私具有深远意义。通过不断研究和改进这些方法,我们...

    IMS信令流程(最新).ppt

    IMS信令基础,包括网元、注册及相关流程、会话及相关流程等。主要内容有:IMS会话及相关流程;...P-CSCF 的发现过程;S-CSCF分配;...IMS会话相关网元功能及IMS会话流程;现有典型网络IMS网络结构及流程等。

    会话:会话注销

    2. 销毁会话:调用`session_destroy()`函数来终止当前会话,并删除服务器上与该会话相关的所有数据。 ```php session_destroy(); ``` 3. 重置会话ID:为了防止会话固定攻击(Session Fixation),在注销后通常...

    根据oracle数据库sid及serial#查找操作系统sid号

    通过查询这些后台进程的PID,我们可以获取到与数据库会话相关的操作系统进程的信息。 #### 使用SQL查询获取相关信息 为了实现上述目标,我们需要查询几个Oracle内部视图:`V$SESSION`、`V$PROCESS` 和 `V$SQLTEXT`...

    实验5 会话及其会话技术.docx

    Servlet 会话技术详解 在 Web 应用程序中,会话是一种非常重要的概念,用于记录和跟踪用户的交互行为。Servlet 提供了两种类型的会话技术,即 Cookie 和 Session,下面我们将详细介绍这两种技术的实现原理和应用...

    企业微信会话内容存档考试

    企业微信会话内容存档是...综上所述,企业微信会话内容存档是一个涉及多个层面的功能,包括合规性、安全性、接口使用、客户服务等多个环节,企业在使用时需严格遵守相关规定,并通过服务商的专业指导来确保其有效实施。

    PHP企业微信会话存档扩展

    “php7-wxwork-finance-sdk-master”这个压缩包文件名暗示这可能是一个针对企业微信金融版的SDK,因为“finance”通常指的是金融相关的服务。这个扩展可能包含了处理会话存档所需的全部功能,包括错误处理、身份验证...

    F5负载均衡算法以及会话保持

    会话保持是指在负载均衡器上有这么一种机制,可以识别客户与服务器之间交互过程的关连性,在作负载均衡的同时,还保证一系列相关连的访问请求会保持分配到一台服务器上。 F5 BigIP 支持多种的会话保持方法,包括: ...

    UniGui 学习笔记.pdf

    此外,UniApplication和UniSession的访问方式和上下文都有限制,UniApplication仅能在uniGUI控件事件处理程序中访问,UniSession则在会话相关的线程中运行,UniThreadTimer事件会运行在一个单独的线程中。...

    Laravel开发-session-monster

    通过"Session Monster"项目,开发者不仅可以深化对Laravel会话机制的理解,还能学会如何在实际项目中优化和解决会话相关的复杂问题。对于想要提升Laravel开发技能的人来说,这是一个非常有价值的资源。

    JSP中会话跟踪的操作

    本教程将深入探讨如何在JSP中进行会话跟踪,以及相关的实践操作。 一、会话的基本概念 会话是指在用户浏览器与服务器之间的一系列连续交互过程。在HTTP协议中,由于其无状态特性,每次请求都是独立的,无法识别...

    javaservlet的一些内容

    以下是一些与Servlet和HTTP会话相关的知识点: 1. **Servlet生命周期**:Servlet的生命周期包括加载、初始化、服务、销毁四个阶段。`init()` 方法在Servlet实例化时调用,用于初始化工作;`service()` 方法处理来自...

Global site tag (gtag.js) - Google Analytics