概述

在这个教程中,我们将专注于Spring MVC的HandlerInterceptor。具体来说,我们将展示使用拦截器的高级用例——通过设置自定义计数器并手动跟踪会话来模拟会话超时逻辑。

如果你想了解Spring中HandlerInterceptor的基础知识,可以查看这篇文章

2. Maven依赖

为了使用Interceptors,你需要在pom.xml文件的dependencies部分添加以下内容:

最新版本可以在这里找到。这个依赖只涵盖了Spring Web,别忘了添加spring-corespring-context以创建一个完整的(最小)web应用。

3. 自定义会话超时实现

在这个例子中,我们将为系统中的用户配置最大不活动时间。超过这个时间后,他们将自动从应用程序中登出。

这个逻辑只是一个概念验证——我们当然可以很容易地使用会话超时达到同样的效果——但重点不在于结果,而在于如何使用拦截器。

因此,我们需要确保如果用户不活跃,会话将被无效化。例如,如果用户忘记登出,不活动时间计数器将阻止未经授权的用户访问账户。为此,我们需要设置一个不活动时间常量:

private static final long MAX_INACTIVE_SESSION_TIME = 5 * 10000;

为了测试,我们将其设置为50秒;请记住,这是以毫秒为单位的。

现在,我们需要在我们的应用中跟踪每个会话,所以我们需要包含这个Spring接口:

@Autowired
private HttpSession session;

接下来,我们继续处理preHandle()方法。

3.1. preHandle()

在这个方法中,我们将执行以下操作:

  • 设置计时器来检查请求的处理时间
  • 检查用户是否已登录(使用这个文章中的UserInterceptor方法)
  • 如果用户的不活跃会话时间超过允许的最大值,自动登出

让我们看看实现:

@Override
public boolean preHandle(
  HttpServletRequest req, HttpServletResponse res, Object handler) throws Exception {
    log.info("Pre handle method - check handling start time");
    long startTime = System.currentTimeMillis();
    request.setAttribute("executionTime", startTime);
}

在代码的这一部分,我们设置了处理执行的startTime。从这一刻起,我们将计数每请求处理所需的秒数。在下一节中,我们将提供会话时间的逻辑,前提是用户在其HTTP会话期间已经登录:

if (UserInterceptor.isUserLogged()) {
    session = request.getSession();
    log.info("Time since last request in this session: {} ms",
      System.currentTimeMillis() - request.getSession().getLastAccessedTime());
    if (System.currentTimeMillis() - session.getLastAccessedTime()
      > MAX_INACTIVE_SESSION_TIME) {
        log.warn("Logging out, due to inactive session");
        SecurityContextHolder.clearContext();
        request.logout();
        response.sendRedirect("/spring-rest-full/logout");
    }
}
return true;

首先,我们需要从请求中获取会话。

接着,我们在控制台进行一些日志记录,记录谁已登录以及用户在我们的应用中执行任何操作后过去了多长时间。我们可以使用session.getLastAccessedTime()获取这些信息,然后减去当前时间并与我们的MAX_INACTIVE_SESSION_TIME进行比较。

如果时间超过了我们允许的范围,我们将清除上下文,登出请求,然后(可选地)作为响应发送重定向到默认登出视图,该视图在Spring Security配置文件中声明。

为了完成处理时间计数器示例,我们还实现了postHandle()方法,这将在下一节中介绍。

3.2. postHandle()

这个方法只是为了获取处理当前请求所需的时间。如上所述,我们在Spring模型中设置了executionTime。现在是使用它的时候了:

@Override
public void postHandle(
  HttpServletRequest request, 
  HttpServletResponse response,
  Object handler, 
  ModelAndView model) throws Exception {
    log.info("Post handle method - check execution time of handling");
    long startTime = (Long) request.getAttribute("executionTime");
    log.info("Execution time for handling the request was: {} ms",
      System.currentTimeMillis() - startTime);
}

实现很简单——我们检查执行时间并从中减去当前系统时间。只是记得将模型值转换为long类型。

现在我们可以正确地记录执行时间。

4. 拦截器配置

要在Spring配置中添加我们新创建的Interceptor,我们需要在实现WebMvcConfigurerWebConfig类中覆盖addInterceptors()方法:

@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(new SessionTimerInterceptor());
}

我们也可以通过编辑XML Spring配置文件来实现相同配置:

<mvc:interceptors>
    <bean id="sessionTimerInterceptor" class="com.baeldung.web.interceptor.SessionTimerInterceptor"/>
</mvc:interceptors>

此外,为了自动化ApplicationContext的创建,我们需要添加监听器:

public class ListenerConfig implements WebApplicationInitializer {
    @Override
    public void onStartup(ServletContext sc) throws ServletException {
        sc.addListener(new RequestContextListener());
    }
}

5. 总结

本教程展示了如何使用Spring MVC的HandlerInterceptor来手动管理会话/超时,拦截网络请求。

如往常一样,所有示例和配置都在GitHub上可用。

5.1. 系列文章

系列文章包括: