Skip to main content

使用自定义的Spring MVC处理程序拦截器管理会话

· 5 min read
Alan

原文 - Using a Custom Spring MVC’s Handler Interceptor to Manage Sessions

简介

本教程专注Spring MVC的 HandlerInterceptor.

我们展示一个使用拦截器的高级示例 - 模拟一个会话超时的逻辑, 通过设置自定义次数手动跟踪会话.

如果你想越多更多关于Spring HandlerInterceptor 的基础知识点击这里.

Maven依赖

使用Interceptors需要在你的pom.xml文件中的dependencies配置节添加以下依赖:

pom.xml
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>

最新版本可以在这里查看. 这个依赖仅覆盖了Spring Web, 因此对于完整的Web应用, 不要忘记添加 spring-corespring-context .

实现自定义的会话超时

在这个例子里, 我们将会设置一个用户在系统中最大的不活跃(inactive)时长. 超过这个时长之后, 用户将会自动被退出登录.

当用户不再是活跃状态后, 需要确保会话是无效的. 比如有个用户忘记退出登录了, 不活跃倒计时将会阻止未授权用户访问账号信息. 因此我们需要设置一个不活跃时长常量:

private static final long MAX_INACTIVE_SESSION_TIME = 5 * 10000;

为了测试, 我们设置了50秒(单位是毫秒).

现在我们需要在我们的应用里跟踪每个会话, 因此我们需要包含这个Spring接口:

@Autowired
private HttpSession session;

让我们处理 preHandle() 方法.

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. 从这时开始, 我们会统计每个请求结束时的耗时秒数. 接下来我们提供会话时间的逻辑, 仅当有人登录:

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比较.

如果超过我们允许的时长, 清空上下文, 退出登录, 然后响应一个跳转.

为了完成处理时长示例, 我们还需要实现 postHandle() 方法.

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

配置拦截器

添加我们新创建的拦截器到Spring配置中, 我们需要重写实现了WebMvcConfigurerWebConfig类中的addInterceptors()方法.

完整示例

定义拦截类

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

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

@Component
public class SampleHandlerInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 在调用处理程序方法之前调用
return false;
}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
// 在调用处理程序方法之后调用
}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 在处理完请求后调用
}
}

使用

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Component
public class YourWebConfiguration implements WebMvcConfigurer {
@Autowired
SampleHandlerInterceptor interceptor;

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(this.interceptor);
}
}