【问题标题】:Java:Why http session is not destroyed when tab or browser is closed?Java:为什么关闭选项卡或浏览器时不会破坏http会话?
【发布时间】:2009-04-01 15:59:41
【问题描述】:

我有以下 HttpSessionlistener 的实现

public class SessionListener implements HttpSessionAttributeListener, HttpSessionListener {


public void attributeAdded(HttpSessionBindingEvent event) {
   ...

}


public void attributeRemoved(HttpSessionBindingEvent event) {

    ...
}

public void attributeReplaced(HttpSessionBindingEvent event) {

}

//HttpSesion creation & destruction
public void sessionCreated(HttpSessionEvent event) {
    HttpSession session = event.getSession();
    //log created time


}

public void sessionDestroyed(HttpSessionEvent event) {
    HttpSession session = event.getSession();
    long destroyedTime = System.currentTimeMillis();
    //log destroyed time

}

}

基本上我会记录会话的创建和销毁时间。 但是如果会话很长(默认为 30 分钟),并且用户同时关闭了浏览器,则

sessionDestroyed 

没有被调用?

这是为什么呢? 是否有一种解决方法可以准确记录会话被销毁的时间(当用户关闭浏览器时)?这不应该是浏览器的问题,在会话关闭时终止会话吗?

有没有我必须实现的接口才能让它工作?

谢谢!

【问题讨论】:

  • 我 100% 确定至少在 2016 年这是可能的。许多网站都会这样做,一旦您关闭 Google Chrome 中的标签,它们就会关闭会话!

标签: java session


【解决方案1】:

服务器如何知道浏览器何时关闭或标签页何时关闭?此时浏览器不会向服务器发送任何内容。

这是 HTTP 的基本部分 - 它是一种请求/响应协议,而不是一种“永久开放的对话”,您可以在其中判断一方是否离开对话。将其视为一系列电报而不是电话 - 您无法确定自己何时收到最后一封电报。

您需要设计自己的方式来解决这个问题 - 以避免需要知道浏览器何时关闭。有一些丑陋的黑客可以解决它 - 例如,让 AJAX 使用心跳消息轮询服务器 - 但更改设计是一个更好的解决方案。

【讨论】:

  • 你的解释是正确的,我真的很感激,但你能建议一种我们可以实现它的方法,就像我们可以在关闭浏览器选项卡时破坏会话一样
  • @dom:不,我已经解释了为什么不可能可靠地做到这一点,除了丑陋的黑客攻击,例如心跳轮询服务器以保持会话活动。
【解决方案2】:

注意: 正如jwenting 在下面评论的那样,这根本不是 100% 安全的。如果 onunload 事件没有被浏览器选项卡或窗口的关闭事件触发,那么这将失败。

我遇到了同样的问题,并使用侵入式 JavaScript 事件解决它:

window.onunload

让我简单解释一下,假设您有一个 JavaScript 函数,该函数使用 jQuery 将当前会话 ID 发布到该 Servlet 以使其无效,如下所示:

function safeexit(){
   $.post("/SessionKillServlet", { SID = <%=session.getId()%> }, function(data){});
}

要调用该函数,您只需像这样绑定它:

window.onunload =  safeexit;  //without the ()

现在,当 TAB 或 BROWSER WINDOW 关闭(或重定向)时,将调用 safeexit 方法。

【讨论】:

  • 迟到总比没有好:不要这样做!这是不安全的,onunload 事件不会被所有可以关闭窗口的方式触发。
  • 确实,但是对于我必须解决的场景,遗憾的是,我不得不实现它:S
  • 我知道。不得不做同样的一次,尽管警告说它不起作用,我们仍然收到我们警告过它不起作用的人的抱怨......
  • @jwenting:我编辑了答案以添加对您评论的引用。迟到总比没有好;)
  • 这是个坏主意,因为onunload 将在各种事件上被调用,而不仅仅是在浏览器关闭时。
【解决方案3】:

会话对象存在于服务器中并由服务器控制。只有服务器可以创建或销毁会话。因此,如果客户端关闭,会话将一直存在,直到过期。客户端只能向服务器建议它可以销毁某些会话。这个请求必须以某种方式明确。因此,当您关闭浏览器窗口时,不会向服务器发出隐式请求,通知它必须销毁给定的会话。

【讨论】:

    【解决方案4】:

    浏览器不做大量工作就不会通知服务器窗口已关闭,因此 Java 无法知道何时销毁会话。因此超时已经到位,并且是服务器第一个知道该会话可以被破坏的时间。

    您可以尝试在 javascript 事件 document.onunload 上播放 ajax 调用 https://developer.mozilla.org/en/DOM/window.onunload 但您需要确保执行此操作时用户不在您的站点内。

    【讨论】:

      【解决方案5】:

      如果您需要在浏览器窗口/选项卡关闭时销毁会话,您可以将 JavaScript 处理程序附加到 onunload 事件,该事件对调用终止会话的资源进行某种 AJAX 调用。

      请注意,onunload 事件并不总是触发,因此它并不完全值得信赖。一种可靠的方法可能是使用“心跳”系统。

      【讨论】:

        【解决方案6】:

        有一些技巧可以让你知道 以下代码用于在用户关闭浏览器时销毁会话

        客户端:

        <script src="jquery path"></script>
        <script>
          window.onunload = function(){ $.get("${request.contextPath}/logout"); }
        </script>
        

        服务器端:为“/logout”创建请求映射

        public void doGet(HttpServletRequest request, HttpServletResponse) {
          request.getSession().invalidate();
          System.out.println('destroy from logout on unload browser');
        }
        

        以下代码是可选的

        当想知道会话何时销毁时使用会话监听器

        //    in class
        
        import javax.servlet.*;
        import javax.servlet.http.*;
        public class SesListener implements HttpSessionListener {
          public void sessionCreated(HttpSessionEvent se) {
           System.out.println("Session created...");
          }
        
          public void sessionDestroyed(HttpSessionEvent se) {
            System.out.println("Session destroyed...");
          }
        }
        
        //-----------------------------------------
        
        // in web.xml
        <listener>
           <listener-class>SesListener</listener-class>
        </listener>
        

        【讨论】:

          【解决方案7】:

          正如 Eric 提到的,浏览器上的 unload 事件可以调用 javascript 函数,该函数又可以通过 servlet 访问 url,从而将您注销。您无需等待 servlet 的实际响应。

          网页浏览器通过http协议与服务器交互,是无状态的。

          【讨论】:

            【解决方案8】:

            您可以验证您的会话用户是否不为空,例如:

            if (session.getAttribute("user") != null )
               sessionsetAttribute("user","null");
            

            【讨论】:

              猜你喜欢
              • 2013-06-11
              • 2010-12-27
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2013-11-03
              • 2019-05-13
              • 2016-01-06
              相关资源
              最近更新 更多