Using a Custom Spring MVC’s Handler Interceptor to Manage Sessions – 使用自定义Spring MVC’的处理程序拦截器来管理会话

最后修改: 2016年 9月 26日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.介绍

In this tutorial, we are going to focus on the Spring MVC HandlerInterceptor.

在本教程中,我们将关注Spring MVC HandlerInterceptor。

More specifically, we will show a more advanced use case for using interceptors – we’ll emulate a session timeout logic by setting custom counters and tracking sessions manually.

更具体地说,我们将展示一个使用拦截器的更高级用例–我们将通过设置自定义计数器和手动跟踪会话来模拟一个会话超时逻辑

If you want to read about the HandlerInterceptor’s basics in Spring, check out this article.

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

2. Maven Dependencies

2.Maven的依赖性

In order to use Interceptors, you need to include the following section in a dependencies section of your pom.xml file:

为了使用Interceptors,你需要在pom.xml文件的dependencies部分包含以下部分。

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>5.3.13</version>
</dependency>

The latest version can be found here. This dependency only covers Spring Web so don’t forget to add spring-core and spring-context for a full (minimal) web application.

最新的版本可以在这里找到。该依赖关系仅涵盖Spring Web,因此不要忘记添加spring-corespring-context以获得完整(最小)的Web应用程序。

3. Custom Implementation of Session Timeouts

3.会话超时的自定义实现

In this example, we will configure maximum inactive time for the users in our system. After that time, they will be logged out automatically from the application.

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

This logic is just a proof of concept – we can of course easily achieve the same result using session timeouts – but the result is not the point here, the usage of the interceptor is.

这个逻辑只是一个概念证明–我们当然可以使用会话超时轻松实现同样的结果–但结果不是这里的重点,拦截器的使用才是重点。

And so, we want to make sure that session will be invalidated if the user is not active. For example, if a user forgot to log out, the inactive time counter will prevent accessing the account by unauthorized users. In order to do that, we need to set constant for the inactive time:

因此,我们要确保在用户不活跃的情况下,会话将被废止。例如,如果用户忘记注销,不活动时间计数器将防止未经授权的用户访问该账户。为了做到这一点,我们需要为不活动时间设置常数。

private static final long MAX_INACTIVE_SESSION_TIME = 5 * 10000;

We set it to 50 seconds for testing purposes; don’t forget, it is counted in ms.

为了测试,我们把它设置为50秒;别忘了,它是以ms为单位计算的。

Now, we need to keep track of each session in our app, so we need to include this Spring Interface:

现在,我们需要跟踪我们应用程序中的每个会话,所以我们需要包括这个Spring界面。

@Autowired
private HttpSession session;

Let’s proceed with the preHandle() method.

让我们继续使用preHandle()方法。

3.1. preHandle()

3.1.preHandle()

In this method we will include following operations:

在这个方法中,我们将包括以下操作。

  • setting timers to check handling time of the requests
  • checking if a user is logged in (using UserInterceptor method from this article)
  • automatic logging out, if the user’s inactive session time exceeds maximum allowed value

Let’s look at the implementation:

让我们来看看实施情况。

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

In this part of the code, we set the startTime of handling execution. From this moment, we will count a number of seconds to finish handling of each request. In the next part, we will provide logic for session time, only if somebody logged in during his HTTP Session:

在这部分代码中,我们设置处理执行的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;

First, we need to get the session from the request.

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

Next, we do some console logging, about who is logged in, and how long has passed, since the user performs any operation in our application. We may use session.getLastAccessedTime() to obtain this information, subtract it from current time and compare with our MAX_INACTIVE_SESSION_TIME.

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

If time is longer than we allow, we clear the context, log out the request and then (optionally) send a redirect as a response to default logout view, which is declared in Spring Security configuration file.

如果时间超过我们允许的范围,我们会清除上下文,注销请求,然后(可选择)发送一个重定向作为对默认注销视图的响应,该视图在Spring Security配置文件中声明。

To complete counters for handling time example, we also implement postHandle() method, which is described in the next subsection.

为了完成处理时间实例的计数器,我们还实现了postHandle()方法,这将在下一小节描述。

3.2. postHandle()

3.2. postHandle()

This method is implementation just to get information, how long it took to process the current request. As you saw in the previous code snippet, we set executionTime in Spring model. Now it’s time to use it:

这个方法的实现只是为了获得信息,即处理当前请求所花的时间。正如你在前面的代码片段中看到的,我们在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);
}

The implementation is simple – we check an execution time and subtract it from a current system time. Just remember to cast the value of the model to long.

实现很简单–我们检查一个执行时间,并从当前系统时间中减去它。只需记住将模型的值转换成long

Now we can log execution time properly.

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

4. Config of the Interceptor

4.拦截器的配置

To add our newly created Interceptor into Spring configuration, we need to override addInterceptors() method inside WebConfig class that implements WebMvcConfigurer:

为了将我们新创建的拦截器添加到Spring配置中,我们需要在实现WebMvcConfigurer的WebConfig类中覆盖addInterceptors()方法。

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

We may achieve the same configuration by editing our XML Spring configuration file:

我们可以通过编辑我们的XML Spring配置文件实现同样的配置。

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

Moreover, we need to add listener, in order to automate the creation of the ApplicationContext:

此外,我们需要添加监听器,以便自动创建ApplicationContext

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

5. Conclusion

5.结论

This tutorial shows how to intercept web requests using Spring MVC’s HandlerInterceptor in order to manually do session management/timeout.

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

As usual, all examples and configurations are available here on GitHub.

像往常一样,所有的例子和配置都可以在GitHub上找到。

5.1. Articles in the Series

5.1.丛书中的文章

All articles of the series:

该系列的所有文章。