伪会话管理机制 控制超时

来源:互联网 发布:适合编程的笔记本 编辑:程序博客网 时间:2024/06/07 15:04
伪会话管理机制
如前所述,基于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 now) f.delete(); // 删除过期的会话文本文件}
五、应用实例
编 译好PseudoSessionBean这个JavaBean之后,我们就可以利用伪会话管理机制来管理Web应用的会话状态信息了。由于不必再使用服务 器的会话管理机制,我们可以在page指令中把session属性设置为false关闭默认的JSP/Servlet会话管理功能。

然后,我们用JSP的标记告诉JSP容器程序要使用PseudoSessionBean:

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