Session 超时问题

众所周知,当用户登录网站后较长一段时间没有与服务器进行交互,将会导致服务器上的用户会话数据(即session)被销毁。此时,当用户再次操作网页时,如果服务器进行了session校验,那么浏览器将会提醒用户session超时。

那么,如何解决用户登录后较长时间未操作而导致的session失效的问题呢?

导致这个问题的关键词有两个:一个是“长时间”,一个是“未操作”。
1、如果用户未操作的“长时间”超过了服务器配置的session超时时间,并导致session失效,那么我们延长session的超时时间,让用户原来的“长时间”与超时时间相比,变得不“长”,不就可以解决了吗?
2、如果用户是长时间“未操作”导致session失效,那么我们想办法产生“操作”,让用户每隔一小段时间就“操作”一次,与服务器产生交互,那么session自然也不会失效。

一般情况下下,我们首先想到的是,通过改变服务器的配置,延长服务器的session超时时间。
例如,在Tomcat服务器的web.xml文件中有如下节点内容:

<session-config>
    <session-timeout>30</session-timeout>
</session-config>

这里的30表示session的超时时间,单位为分钟,如果用户登录后在30分钟内没有与服务器交互,那么当前用户的session将失效。我们可以配置一个更大的数值(比如60),就可以延长session的超时时间,如果将该值改为0或负数的话,则表示session永不失效。

不过在实际的工作应用中,一味地上调session的超时时间设置并不怎么常见,大多数需要实现该功能的网站都将解决问题的焦点集中在第二条思路上。例如:一些在线网站均采用定时刷新页面的方法来防止session超时。

定时刷新页面,最常见的有两种实现方式:一种是通过JavaScript + HTML DOM,另一种则是通过meta标签来实现。

function refresh(seconds){
    setTimeout("self.location.reload()", seconds * 1000);
}
refresh(600);  //调用方法启动定时刷新,数值单位:秒。

在页面中添加meta标签refresh也可以指定每隔指定时间就刷新当前页面,示例代码如下:

<meta http-equiv="refresh" content="600" />

上述meta标签可以实现每过600秒就刷新一次当前页面。

在上述两种方案中,较好的为第二种,因为如果当前页面是在IE浏览器的模式窗口中打开的,默认情况下,self.location.reload()方法将会失效,而refresh meta标签在IE模式窗口下仍然有效。

上述两种方式都实现了刷新当前页面,并且使用起来非常简单,不过很遗憾的是,它们存在一种几乎致命的缺陷。 试想一下,如果在论坛发帖等需要用户输入内容的页面,用户花费较长的时间输入了许多文本内容,可是突然遇到了一个定时页面刷新,结果用户输入的所有内容都没了,估计这个时候用户连掐死你的心都有了……

因此我们需要在当前页面本身不刷新、不影响用户的任何操作的情况下实现定时刷新。最常见的解决方法仍然有两种。
一种是在当前页面添加一个隐藏的iframe,然后在该iframe里面实现定时刷新。

<iframe id="hidden_iframe" style="display:none;" scrolling="no" frameborder="0" name=" hidden_iframe " src="ping.php"></iframe>

此外,我们需要在服务器上编写对应的请求响应代码,例如ping.php中可以编写如下代码:

<?php
//每隔600秒刷新当前页面
echo '<html><head><meta http-equiv="refresh" content="600" /></head><body></body></html>';
?>

另外一种则是使用JavaScript Image对象来实现定时刷新,JavaScript代码如下:

function autoRefresh(seconds){
    if(typeof period == "undefined"){   //如果是第一次执行
        period = seconds * 1000;    //定义全局变量period
        var bodyDOM = document.getElementsByTagName("body")[0];
        if(bodyDOM){
            bodyDOM.innerHTML += '<img id="auto_refresh_img" src="" style="display:none" />';   //添加隐藏的图片
            imgDOM = document.getElementById("auto_refresh_img");   //定义全局Image对象
        }
    }
    if(typeof imgDOM != "undefined"){
        imgDOM.src = "ping.php?sid=" + new Date().getTime();    //防止缓存
        setTimeout("autoRefresh(" + seconds + ")", period);    
    }
}
autoRefresh(600);   //调用方法启动定时刷新

和使用iframe来实现定时刷新一样,使用JavaScript Image对象实现定时刷新,也需要在服务器端编写类似的请求响应代码。服务器的响应可以是文字等非图片内容,非图片内容只会造成图像加载失败,而我们的图像标签本身就是隐藏的,不管是加载成功还是失败都不会显示,毕竟我们的主要目的是发送请求给服务器,让服务器保持session处于活动状态。

当然,我们还可以使用Ajax来实现定时刷新,不过使用Image对象会更好一些,因为有些老式浏览器的JavaScript无法实现Ajax,但是却可以使用Image对象。此外,使用Ajax需要编写更多的代码来处理XMLHttpRequest等对象的活动。

在上述两种方式中,各有其优缺点。

使用iframe标签实现定时刷新的优点是:不需要编写JavaScript代码,可以在浏览器禁用JavaScript的情况下实现定时刷新;其缺点是:在某些不支持iframe标签的老式浏览器中没有效果,此外,iframe标签在浏览器中新增加了一个独立的页面,即使没有显示出来,不过其内部解析的windowdocument等对象仍然存在,占用的浏览器内存相对较多。

使用Image对象的优点是:与iframe相比,占用的内存相对较少,支持Image的浏览器也相对较多(现代浏览器均支持);缺点是:在浏览器禁用JavaScript的情况下就毫无用武之地了。

开发人员应根据实际需求情况来确定使用何种实现方式更好。此外,服务器在响应定时刷新的请求时,在满足要求的情况下,应返回尽可能少的数据,以节省带宽。

posted @ 2014-12-12 15:34  惊蛰.  阅读(2044)  评论(0编辑  收藏  举报