Spring拦截器Interceptor总结

在 Spring 中,拦截器(Interceptor) 是一种强大的机制,用于在请求处理的不同阶段(如预处理、后处理)插入自定义逻辑。

什么是拦截器

在Spring Web MVC 中,拦截器(Interceptor)同 Servlet 中的过滤器(Filter) 类似, 都可以实现对用户的请求做出相应的处理。

所有 HandlerMapping 的实现都支持处理程序拦截器,当想将特定功能应用于某些请求时很有用 —— 例如检查权限。 拦截器必须实现
org.springframework.web.servlet 包中 HandlerInterceptor 接口的三个方法, 这些方法能够提供足够的灵活性来进行各种预处理和后处理:

  • preHandle(..): Before the actual handler is run // 在实际 handler 之前运行
  • postHandle(..): After the handler is run // 在 handler 之后运行
  • afterCompletion(..): After the complete request has finished // 在整个请求完成后运行

preHandle 方法返回一个布尔值,可以用该方法中断或者继续执行链的处理。

  • 当返回 true 时,执行链会继续执行;
  • 当返回 false 时,DispatcherServlet 假定拦截器本身已经处理了请求(例如,呈现了适当的视图)并且不会继续执行其他拦截器和执行链中的实际处理程序。

【注意】:postHandle 对于 @ResponseBody 和 ResponseEntity 方法的用处不大,因为这些方法的响应是在 HandlerAdapter 中和
postHandle 之前写入和提交的。 这意味着对响应进行任何更改都为时已晚,例如添加额外的请求头。 对于此类场景,您可以实现
ResponseBodyAdvice 并将其声明为 Controller Advice bean 或直接在 RequestMappingHandlerAdapter 上进行配置。

使用方法

创建自定义拦截器类

实现 HandlerInterceptor 接口,按需重写方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

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

@Component
public class CustomInterceptor implements HandlerInterceptor {

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// 示例:验证用户是否登录
String token = request.getHeader("Authorization");
if (token == null) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "未授权");
return false; // 中断请求
}
return true; // 继续执行后续流程
}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// 在控制器执行后、视图渲染前插入逻辑
System.out.println("控制器处理完成");
}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception ex) throws Exception {
// 请求完全结束后执行(如资源清理)
System.out.println("请求处理完毕");
}
}

注册拦截器到 Spring MVC

通过 WebMvcConfigurer 配置类注册拦截器,并指定拦截路径:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

@Autowired
private CustomInterceptor customInterceptor;

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(customInterceptor)
.addPathPatterns("/**") // 拦截所有路径
.excludePathPatterns("/login"); // 排除特定路径(如登录接口)

// 当存在多个拦截器时,定义它们的执行顺序(数值越小优先级越高)
// registry.addInterceptor(otherInterceptor).order(2);

// 可注册多个拦截器,执行顺序按注册顺序
registry.addInterceptor(otherInterceptor);
}
}

拦截器的执行顺序

  • 单个拦截器:preHandle → 控制器 → postHandle → 视图渲染 → afterCompletion。

  • 多个拦截器:

    • preHandle 按拦截器注册顺序执行。
    • postHandle 和 afterCompletion 按注册的逆序执行。

常见场景

日志拦截器

记录请求参数、耗时、响应状态;添加全局traceId便于日志查询。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class LogInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
System.out.println("请求开始: " + request.getRequestURI());
request.setAttribute("startTime", System.currentTimeMillis());

String traceId = request.getHeader("traceId");
if (traceId == null) {
traceId = UUID.randomUUID().toString();
}

// 需要搭配logback-spring.xml使用
// %d{yyyy-MM-dd HH:mm:ss} %X{traceId} [%-5level] %logger{36}:%M - %msg%n
MDC.put("traceId", traceId);

return true;
}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
long startTime = (Long) request.getAttribute("startTime");
long duration = System.currentTimeMillis() - startTime;
System.out.println("请求耗时: " + duration + "ms");
}
}

注册拦截器

用于拦截非法IP调用,确保系统安全。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class RegisterInterceptor implements HandlerInterceptor {

// 配置文件中指定可以访问的所有ip
@Value("${allowed.ip}")
protected String allowedIp;

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
Set<String> ipSet = new HashSet<String>(Arrays.asList(allowedIp.split(",")));

// 获取当前访问的ip地址
String ip = WebUtil.getRequestIp(request);

if (!ipSet.contains(ip)) {
System.out.println("非法IP: " + ip);
return false;
}

if (!(handler instanceof HandlerMethod)) {
return false;
}

// 其他配置
return true;

}
}

登录拦截器

校验用户的登录状态,比如token、session过期;以及查询用户有效性等状态信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class JWTInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler)
throws Exception {

// 判断登陆状态
String token = request.getHeader(BaseConstant.TOKEN);
if (StringUtils.isBlank(token)) {
// 用户未登陆
return false;
}

UserToken userToken = TokenVerify.queryUser(token);
if (userToken == null) {
// 用户登陆过期
return false;
}

// 查询用户并校验状态
Usee user = mapper.getUserById(userToken.getId());

if (user != null && EnumConstant.IS_NOT_VALID.getCode() == userInfoTable.getIsValid()) {
// 用户失效
return false;
}

if (userInfoTable != null && EnumConstant.IS_DELETE.getCode() == userInfoTable.getIsDelete()) {
// 用户不存在或被删除
return false;
}

return true;
}
}

参考


Spring拦截器Interceptor总结
https://zhyyao.me/2024/09/23/experience/spring_interceptor/
作者
zhyyao
发布于
2024年9月23日
许可协议