JSP会话过期/超时后自动重定向

6
有没有办法在没有用户交互的情况下检测会话超时并重定向到某个页面?即如果在页面上没有任何活动在指定的持续时间内,服务器会自动检测并将其重定向到其他页面。
所谓用户交互是指:当用户单击某个东西时,可以检测会话超时,然后发送一些请求到服务器,然后服务器检查当前用户会话是否过期。
我需要的是,在不通知服务器任何内容(或不执行任何操作)的情况下,会话过期时服务器会自动检测并执行所需的操作。
谢谢, Raza
4个回答

8
如果要求只是在会话超时后重定向到登录页面(或任何其他页面),则可以按照以下方式实现:
将以下脚本片段包含到需要登录的所有页面中。
<%
int timeout = session.getMaxInactiveInterval();
response.setHeader("Refresh", timeout + "; URL = login.jsp");
%>

这样,任何需要登录的页面在会话超时后将刷新/重定向到login.jsp(将其更改为所需的URL)。
或者(为了避免错过任何页面),您实际上可以将其编写为一个单独的文件(timedoutRedirect.jsp),并使用“JSP属性组”(在web.xml中)将其作为所有需要登录的页面的头文件包含进去。
<jsp-property-group>
        <display-name>all jsp</display-name>
        <url-pattern>/users/*</url-pattern>
        <include-prelude>/timedoutRedirect.jsp</include-prelude>           
</jsp-property-group>

您可能需要根据项目规格调整引入URL。


3

当然,您可以通过实现文档范围的键盘和/或鼠标监听器以及具有超时的周期性方法在JavaScript中执行此操作。

var timeOut = 1000 * 60 * 30; // 30 minutes
var lastActivity = new Date().getTime();
var checkTimeout;
checkTimeOut = function(){
    if(new Date().getTime() > lastActivity + timeOut){
        // redirect to timeout page
    }else{
        window.setTimeout(checkTimeOut, 1000); // check once per second
    }
}

现在你的全局监听器只需要在每个操作上将lastActivity设置为当前时间。

重新阅读问题后,您希望使用应用程序服务器的实际会话超时时间。这很困难,因为当您向服务器发送ajax请求时,您实际上会防止会话过期(除非有硬限制),因此我的答案可能仍然是最佳方法。


我过去曾经使用过这样的方法,效果很好。+1 - cjstehno
谢谢,我真的需要这个东西,顺便说一下,我在同一个网站上也找到了相关链接https://dev59.com/QkvSa4cB1Zd3GeqPbRK6 - user473445

0

我曾经遇到过类似的问题,这是我解决的方法...

基本上,我使用JavaScript在客户端创建了一个会话计时器。我定期发送ajax调用(定时器小于实际服务器超时设置),以保持服务器上的会话活动。

然后,我创建了另一个计时器,在客户端倒数计时(设置为与实际服务器超时设置相同的时间)。如果该计时器到期,则向服务器发出ajax调用,运行session.invalidate(),然后转发到登录页面。每当有一些应该保持会话活动的操作时(例如鼠标单击、按键等),此计时器都会被重置。


0

无论是简单的servlet、spring-mvc还是spring-security,如果没有完美的客户端逻辑,自动注销都是不可能的。
考虑到应用程序将具有以下两种类型的请求:

  • AJAX和
  • 表单提交/页面重新加载

自动注销需要非常精确的逻辑。以下是我的自动注销功能实现及其优点:

优点。


1. 不需要额外的调用/请求来实现此功能。考虑到性能影响,如果有超过10k个活跃用户并且需要额外的调用来实现自动注销。
2. 使用标签进行一行配置。
3. 即使用户打开多个选项卡或多个窗口,也可以无缝运行。
4. 在会话失效前30秒通知您,因此如果您填写了表单但未提交,则可以保持会话活动状态(通过单击扩展会话)。 因此,用户不太可能丢失未保存的数据。

用法


1. 在所需的JSP页面中包含以下自动注销脚本。

    ....
    </body>
    <jsp:include page="../template/autologout-script.jsp"></jsp:include>
</html>

2. 创建一个JSP页面,autologout-script.jsp,并添加以下代码。 注意:无需编辑/配置

<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>

<script>
$(document).ready(function()
{
    var timeOutTimeInSeconds = ${ timeOutTimeInSeconds }; 
    var showTimerTimeInSeconds= ${ showTimerTimeInSeconds };

    var sessionCheckIntervalId = setInterval(redirectToLoginPage, timeOutTimeInSeconds * 1000);
    var timerDisplayIntervalId = setInterval(showTimer, (timeOutTimeInSeconds - showTimerTimeInSeconds) * 1000);
    var badgeTimerId;
    window.localStorage.setItem("AjaxRequestFired", new Date());

    function redirectToLoginPage(){
        //location.href =  '<c:url value="/" />'+'${loginPageUrl}';
        window.location.reload();
    }

    $(document).ajaxComplete(function () {
        resetTimer();
    });

    $(window).bind('storage', function (e) {
         if(e.originalEvent.key == "AjaxRequestFired"){
             console.log("Request sent from another tab, hence resetting timer")
             resetTimer();
         }
    });

    function resetTimer()
    {
        showTimerTimeInSeconds= ${ showTimerTimeInSeconds };

        console.log("timeOutTimeInSeconds : "+timeOutTimeInSeconds)
        window.localStorage.setItem("AjaxRequestFired", new Date());

        window.clearInterval(sessionCheckIntervalId);
        sessionCheckIntervalId = setInterval(redirectToLoginPage, timeOutTimeInSeconds * 1000);

        window.clearInterval(timerDisplayIntervalId);
        timerDisplayIntervalId = setInterval(showTimer, (timeOutTimeInSeconds - showTimerTimeInSeconds) * 1000);

        hideTimer();
    }

    function showTimer()
    {
        $('#sessionTimeRemaining').show();
        $('#sessionTimeRemainingBadge').html(showTimerTimeInSeconds--);
        window.clearInterval(timerDisplayIntervalId);
        badgeTimerId = setInterval(function(){
            $('#sessionTimeRemainingBadge').html(showTimerTimeInSeconds--);
        }, 1000);
    }

    function hideTimer()
    {
        window.clearInterval(badgeTimerId);
        $('#sessionTimeRemaining').hide();
    }
});
</script>

3. 配置会话属性以配置超时设置 注意:在会话创建后进行配置。您可以实现HttpSessionListener的sessionCreated方法,并根据需要设置以下配置。

session.setMaxInactiveInterval(300);

session.setAttribute("timeOutTimeInSeconds", 300);
session.setAttribute("showTimerTimeInSeconds", 30);

4. 添加以下 HTML 以显示计时器。
注意:如果您擅长 CSS,可以将其移动到自动注销脚本模板页面中。因此,您可以避免在每个页面中都添加它。
包含 Bootstrap 或添加您自定义的 CSS。

<span class="badge badge-primary" title="click to keep session alive" id="sessionTimeRemaining" 
    onclick="ajaxSessionRefresh()" style="display:none;">
    <i class="badge badge-danger" id="sessionTimeRemainingBadge" style="float:left">30</i>
     &nbsp; 
     <small>Refresh</small>
     <i class="glyphicon glyphicon-refresh"></i>
</span>

enter image description here

这篇文章主要讲解了一个简单的自动登出实现方法。 你可以从我的Github仓库下载完整代码示例
使用简单Servlet实现自动登出的示例
使用Spring-Security Java配置实现自动登出的示例
使用Spring-Security XML配置实现自动登出的示例

逻辑说明


情况1:页面加载时
逻辑很简单,在页面加载时设置定时器,间隔时间等于maxInactiveInterval。超时后重定向到登录页面。
情况2:跟踪AJAX调用
现在考虑AJAX请求,您可以使用jquery的.ajaxStart()或.ajaxComplete()回调函数,以便如果发生任何ajax请求,您可以重置间隔时间。
情况3:跟踪多个选项卡/窗口活动
通过互选项卡通信来同步每个选项卡的状态。使用localStorage的change事件。

限制/需要改进
1. 如果最大允许会话为一,则如果会话来自另一个系统,则AJAX请求将失败。需要处理以重定向到登录页面。
2. 使用ajaxStart()而不是ajaxComplete(),以在服务器和浏览器之间具有精确的idleTime值同步。

要求
1. Jquery

与当前实现相比的替代方案


1. 在http响应中设置刷新标头(不适用于AJAX请求)

response.setHeader("Refresh", "60; URL=login.jsp");
  • 在HTML中设置meta refresh标签(不适用于AJAX请求)
  • <meta http-equiv="refresh" content="60; url=login.jsp">
    
  • 配置活动检查器 通过重复的AJAX请求保持会话活动。跟踪空闲时间并在超时后发出注销请求。
    毫无疑问,这是一个具有简单逻辑的好方法。但我只想记录一下我的观察。
    • 性能影响如果每分钟进行2次请求以保持会话活动,并且有50,000个活跃用户。每分钟100,000个请求。
    • 标签间通信如果打开了两个标签,一个标签正在接收活动,而另一个标签没有接收到活动,那么该标签将发出注销请求并使会话失效,即使其他标签中存在活动。(但可以处理)
    • 强制注销方法这是客户端主导服务器来使会话失效。

  • 网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接