本站消息

站长简介/公众号

  出租广告位,需要合作请联系站长


+关注
已关注

分类  

暂无分类

标签  

暂无标签

日期归档  

暂无数据

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

发布于2021-12-16 21:48     阅读(1409)     评论(0)     点赞(14)     收藏(5)


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

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;
        }
    }
}

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

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.context.support.WebApplicationContextUtils;

import com.practice.AppConstant;

public class AuthenticationTokenFilter extends UsernamePasswordAuthenticationFilter {

    private Tokenutils tokenUtils;

    //@Autowired
    //private UserDetailsService userDetailService;

    @Override
    public void doFilter(ServletRequest request , ServletResponse res , FilterChain chain) throws IOException , ServletException{

            tokenUtils=(Tokenutils) WebApplicationContextUtils.getRequiredWebApplicationContext(this.getServletContext()).getBean(Tokenutils.class);

            //userDetailService=WebApplicationContextUtils.getRequiredWebApplicationContext(this.getServletContext()).getBean(UserDetailsService.class);

            HttpServletResponse response = (HttpServletResponse) res;
            response.setHeader("Access-Control-Allow-Origin", "*");
            response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
            response.setHeader("Access-Control-Max-Age", "3600");
            //response.setHeader("Access-Control-Allow-Headers", "x-requested-with");
            response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type,"+AppConstant.tokenHeader);

            HttpServletRequest httpRequest = (HttpServletRequest)request;
            //String authToken=httpRequest.getHeader(AppConstant.tokenHeader);
            String authToken=httpRequest.getParameter("XAuthToken");
            String userName=this.tokenUtils.getUsernameFromToken(authToken);
            if(userName!=null && SecurityContextHolder.getContext().getAuthentication()==null){
                //UserDetails userDetails= this.userDetailService.loadUserByUsername(userName);
                UserDetails userDetails=new SpringSecurityUser(1L, userName, null, null, null, AuthorityUtils.commaSeparatedStringToAuthorityList(""));
                if(this.tokenUtils.validateToken(authToken,userDetails)){
                    UsernamePasswordAuthenticationToken authentication= new UsernamePasswordAuthenticationToken(userDetails, null,userDetails.getAuthorities());
                    authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpRequest));
                    SecurityContextHolder.getContext().setAuthentication(authentication);
                }
            }
            chain.doFilter(request, res);
    }
}

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


解决方案


If you are using Spring security then it's better to rely on the session timeout instead of checking the inactive time yourself. Few issues with this approach is - you are implementing security at much deeper level in stack as it should be handled at Filter level which is where Spring security handles it. Secondly as per servlet specs

The session is considered to be accessed when a request that is part of the session is first handled by the servlet container.

Also the session.getLastAccessedTime() is updated anytime when the request.getSession is called (atleast this is how it is in Tomcat 7, there could be multiple behaviours as explained here) which can be called at any place - any other Filter configured, Servlet, HandlerInterceptor, Controller. And last but very important - SecurityContextHolder.clearContext() will remove the SecurityContext from the current thread ( where it was saved by ThreadLocal) but it will be still present in the session ( where it will is saved by default) which is not what you actually want.

Another option could be to store the login time in the User Object stored in Spring security context on login and then check for the time and inactivate the session and redirecting to login page afresh.

UPDATE:- There is another easy way in which you can write a custom filter like below and register it before the Spring security filter in web.xml. What we are doing here is saving last access time in session for first visit, then updating the last access time in session on every visit but if it exceeds the inactive time redirect to login page. So basically you are not rely on session object last access time but a user defined variable stored in session. Also we invalidate the current session if inactivity time exceeds. I have tested this filter along with Spring security filters and it works fine. You can do similar thing in an Spring HandlerInterceptor but I feel this kind of check should be implemented at a place even before Spring security gets involved. But it's a choice.

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

}


所属网站分类: 技术文章 > 问答

作者:黑洞官方问答小能手

链接:http://www.javaheidong.com/blog/article/357393/5e562c649a70edb31ebd/

来源:java黑洞网

任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任

14 0
收藏该文
已收藏

评论内容:(最多支持255个字符)