如何在spring mvc中使用HandlerInterceptorAdapter手动处理会话超时

时间:2017-09-22 07:33:54

标签: java spring spring-mvc session

我想在会话超时上做一些活动,我使用了看起来像这样的HandlerInterceptorAdapter

javax.validation

问题是当请求来到拦截器的preHandle方法时,会话的lastAccessTime被更新到当前时间。 我认为原因是在去预处理方法之前,每个请求都是我的自定义UsernamePasswordAuthenticationFilter的方法。我也在我的应用程序中使用了Spring安全性。

package com.practice.security;


import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

public class SessionHandelerInterceptor extends HandlerInterceptorAdapter{
    private static final long MAX_INACTIVE_SESSION_TIME = 5 * 10000;
    @Autowired
    private HttpSession session;
    @Override
    public boolean preHandle(HttpServletRequest request,HttpServletResponse response, Object handler) throws Exception {

        //long startTime = System.currentTimeMillis();

        //request.setAttribute("executionTime", startTime);
        if (this.isUserLogged()) {
            session = request.getSession();
            if (System.currentTimeMillis() - session.getLastAccessedTime()
              > MAX_INACTIVE_SESSION_TIME) {

                //Do some more activity

                SecurityContextHolder.clearContext();
                request.logout();
                response.sendRedirect("/userLogin/logout");
            }
        }
        return true;
    }

    private boolean isUserLogged(){
        try {
            return !SecurityContextHolder.getContext().getAuthentication()
              .getName().equals("anonymousUser");
        } catch (Exception e) {
            return false;
        }
    }
}

任何帮助将不胜感激谢谢。

1 个答案:

答案 0 :(得分:1)

如果您使用的是Spring安全性,那么最好依靠会话超时而不是自己检查非活动时间。这种方法的几个问题是 - 你在堆栈的更深层次上实现安全性,因为它应该在Filter级别处理,这是Spring安全性处理它的地方。其次根据servlet规范

  

当请求是部分请求时,将认为会话被访问   会话的第一个由servlet容器处理。

同样session.getLastAccessedTime()会在调用request.getSession的时候更新(至少这是Tomcat 7中的情况,可能有多个行为,如here所述),可以调用在任何地方 - 配置任何其他过滤器,Servlet,HandlerInterceptor,Controller。最后但非常重要 - SecurityContextHolder.clearContext()将从当前主题(SecurityContext保存它)中删除ThreadLocal但它仍会出现在会话中(保存的地方)默认情况下)这不是你真正想要的。

另一种选择可能是在登录时将登录时间存储在Spring安全上下文中存储的用户对象中,然后检查时间并停用会话并重新定向到登录页面。

更新: - 还有另一种简单的方法可以编写如下所示的自定义过滤器,并在之前在中注册web.xml中的Spring安全过滤器。 我们在这里做的是保存第一次访问的会话中的最后访问时间,然后在每次访问时更新会话中的最后访问时间,但是如果它超过非活动时间重定向到登录页面。所以基本上你不是依赖会话对象上次访问时间而是依赖会话中存储的用户定义变量。如果不活动时间超过,我们也会使当前会话无效。我已经测试了这个过滤器和Spring安全过滤器,它工作正常。你可以在Spring HandlerInterceptor中做类似的事情,但我觉得这种检查应该在Spring安全性介入之前在一个地方实现。但这是一个选择。

public class InactivityFilter implements Filter {

    private static final long MAX_INACTIVE_SESSION_TIME = 5 * 1000;

    public void init(FilterConfig config) throws ServletException {
        System.out.println("InactivityFilter.init()");
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
            throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;

        String requestURI = request.getRequestURI();
        System.out.printf("InactivityFilter.doFilter(): requestURI = %s; ", requestURI);

        Long firstAccessTime = (Long) request.getSession().getAttribute("lastAccessTime");

        if (firstAccessTime == null) {
            request.getSession().setAttribute("lastAccessTime", new Date().getTime());
            chain.doFilter(req, resp);
        }

        if (firstAccessTime != null) {
            if (System.currentTimeMillis() - firstAccessTime > MAX_INACTIVE_SESSION_TIME) {
                request.getSession().invalidate();
                response.sendRedirect("the url for login page");
                return;
            } else {
                request.getSession().setAttribute("lastAccessTime", new Date().getTime());
                chain.doFilter(req, resp);
            }

        }

    }

    public void destroy() {
        System.out.println("InactivityFilter.destroy()");
    }

}