HttpSessionListener.sessionDestroyed() 方法在会话超时期间被调用两次

HttpSessionListener.sessionDestroyed() method is getting called twice during Session timeout

我正在尝试在 HTTP 会话被破坏时记录一条消息。 我在此 Web 应用程序中使用 Spring 启动、Spring 安全和 Tomcat 8(嵌入式)。

在会话超时期间,sessionDestroyed() 方法被调用了 2 次,因此我的消息被记录了两次。

我检查了会话 ID,两次调用期间会话 ID 相同

这就是我的代码的样子...

import org.springframework.security.core.session.SessionRegistry;

...

        @Component
        public class MySessionListener implements javax.servlet.http.HttpSessionListener, ApplicationContextAware {
            @Autowired(required = false)
            SessionRegistry sessionRegistry;

下面是 sessionDestroyed()。

@Override
    public void sessionDestroyed(HttpSessionEvent se) {
        HttpSession session = se.getSession();

        SecurityContextImpl springSecurityContext = (SecurityContextImpl)session.getAttribute("SPRING_SECURITY_CONTEXT");
        if(springSecurityContext!=null){
            Authentication authentication = springSecurityContext.getAuthentication();
            LdapUserDetails userDetails = (LdapUserDetailsImpl)authentication.getPrincipal();

            WebAuthenticationDetails WebAuthenticationDetails = (WebAuthenticationDetails)authentication.getDetails();
            String userIp = WebAuthenticationDetails.getRemoteAddress();

            Log.info(userDetails.getUsername(),userIp,timestamp,"timeout or logout","session destroyed");

        }

       sessionRegistry.removeSessionInformation(se.getSession().getId());
        logger.info("Due to timeout/logout Session is Destroyed : Session ID is..." + session.getId());

    }

任何帮助将不胜感激...

注意: 我注意到这个问题是 Tomcat 5 中的一个缺陷,我不认为这个缺陷是Tomcat 8.

中仍未修复

参考:https://bz.apache.org/bugzilla/show_bug.cgi?id=25600

这不是 Tomcat 的错误。这是我的应用程序特有的错误。

我在我的应用程序中找到了以下代码。

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    if (applicationContext instanceof WebApplicationContext) {
        ((WebApplicationContext) applicationContext).getServletContext().addListener(this);
    } else {
        //Either throw an exception or fail gracefully, up to you
        throw new RuntimeException("Must be inside a web application context");
    }
}

下一行将当前侦听器添加到 Servlet 上下文中。

getServletContext().addListener(this);

因为 Spring 已经添加 这个监听器 (MySessionListener) 到 Servlet 上下文中,第二次添加监听器导致 [=33 调用第二次 sessionDestroyed() 方法=]的org.apache.catalina.session.StandardSessionclass.

Tomcat的源码供参考

package org.apache.catalina.session;

public class StandardSession implements HttpSession, Session, Serializable {

....

public void expire(boolean notify) {

.....

.....

 Object listeners[] = context.getApplicationLifecycleListeners();
                    if (listeners != null && listeners.length > 0) {
                        HttpSessionEvent event =
                            new HttpSessionEvent(getSession());
                        for (int i = 0; i < listeners.length; i++) {
                            int j = (listeners.length - 1) - i;
                            if (!(listeners[j] instanceof HttpSessionListener))
                                continue;
                            HttpSessionListener listener =
                                (HttpSessionListener) listeners[j];
                            try {
                                context.fireContainerEvent("beforeSessionDestroyed",
                                        listener);
                                listener.sessionDestroyed(event);
                                context.fireContainerEvent("afterSessionDestroyed",
                                        listener);
                            }

....
....

取自 Tomcat..

以上源代码

因此,listeners[] 包含 MySessionListener 的重复条目。

Object listeners[] = context.getApplicationLifecycleListeners();