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

时间:2016-06-28 18:59:27

标签: spring servlets tomcat8

每当HTTP Session被销毁时,我都会尝试记录消息。 我在此Web应用程序中使用 Spring Boot,Spring Security和Tomcat 8 (嵌入式)。

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

我在两次调用期间都检查了会话ID,会话ID是SAME

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

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

1 个答案:

答案 0 :(得分:0)

这不是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 Context中。

getServletContext().addListener(this);

由于 Spring已将此侦听器(MySessionListener)添加到Servlet上下文中,因此第二次添加侦听器,由Tomcat的org.apache.catalina.session.StandardSession类引入第二次sessionDestroyed()方法调用。

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();