基础
拦截器的生效位置

为什么prehandle是主流?(核心原因)
以后面的案例的「token 校验」为例,就能明白为什么大家优先用 prehandle:
1. 「提前拦截」= 减少无效消耗(最核心)
如果 token 无效 / 不存在,prehandle 返回 false,请求会直接被拦截,不会进入控制器、不会调用 service/dao、不会操作数据库—— 相当于 “把坏人挡在门外”,而不是 “让坏人进屋里再赶出去”。
- 比如:用户拿失效的 token 访问
/api/user/info,prehandle直接返回 401,控制器的getuserinfo()方法根本不会执行,节省了服务器资源; - 如果用
posthandle:控制器方法已经执行完(比如查了数据库、查了用户信息),再发现 token 无效,此时只能修改响应,但数据库查询、业务逻辑执行的资源已经消耗了,完全没必要。
2.prehandle是唯一能「阻断请求」的方法
posthandle 和 aftercompletion 都是 void 返回值,哪怕发现 token 无效,也无法阻止请求流程(因为控制器已经执行了),只能做一些 “补救”(比如修改响应体),但核心业务逻辑已经执行,失去了拦截的意义。而 prehandle 的 boolean 返回值是 “生死开关”,这是实现「权限控制、token 校验、请求限流」等核心拦截逻辑的关键。
3. 符合 “拦截器” 的设计初衷
拦截器(interceptor)的核心目的是「在请求处理前做校验 / 过滤,避免无效请求消耗资源」,而不是 “请求处理后做善后”——prehandle 完美契合这个初衷,而另外两个方法只是补充。
posthandle和aftercompletion什么时候用?
不是说这两个方法没用,而是它们的场景很特定,只适合「后置处理」,比如:
1.posthandle适用场景
- 统一修改响应数据(比如给所有接口返回值加统一的包装格式:
{code:200, data:..., msg:"success"}); - 修改视图渲染参数(比如给所有页面添加全局的用户信息);
- 记录控制器执行耗时(结合
prehandle记录的开始时间,计算耗时)。
2.aftercompletion适用场景
- 资源清理(比如关闭请求中打开的流、释放临时文件);
- 全局异常记录(通过方法参数
exception ex捕获整个请求流程的异常,记录日志); - 最终的请求日志汇总(比如记录请求的最终状态、响应码)。
拦截器的使用步骤
1.1 创建拦截器
第1步:工程目录下创建拦截器
base.interceptors.myinterceptor(命名随意)
public class myinterceptor implements handlerinterceptor {
/**
* 1.执行handler之前调用的拦截方法;
* 2.可以进行编码格式设置、登录保护、权限处理等。
* @param request 请求对象
* @param response 响应对象
* @param handler handler是我们要调用的方法对象
* @return true放行,false拦截
* @throws exception
*/
@override
public boolean prehandle(httpservletrequest request, httpservletresponse response, object handler) throws exception {
system.out.println("1.prehandle执行");
return true;
}
/**
* 1.当handler方法执行完成后,触发的方法,没有拦截机制了。
* 2.此方法只有 prehandle中 return true时才会触发
* 3.对结果进行处理,比如敏感词检查。
* @param request 请求
* @param response 响应
* @param handler handler方法
* @param modelandview 返回的视图和模型数据
* @throws exception
*/
@override
public void posthandle(httpservletrequest request, httpservletresponse response, object handler, modelandview modelandview) throws exception {
system.out.println("2.posthandle执行");
}
/**
* 整体处理完毕,调用此方法
* @param request 请求
* @param response 响应
* @param handler handler方法
* @param ex 异常对象
* @throws exception
*/
@override
public void aftercompletion(httpservletrequest request, httpservletresponse response, object handler, exception ex) throws exception {
system.out.println("3.aftercompletion执行");
}
}第2步:注册拦截器,工程目录下创建
config.mvcconfig(命名随意)第1:实现webmvcconfigurer,它是springmvc的标准化配置类,提供拦截器的注入
第2:重写注入方法:addinterceptors
第3:注册拦截器:registry.addinterceptor()
@configuration
public class interceptorconfig implements webmvcconfigurer {
@override
public void addinterceptors(interceptorregistry registry) {
//1.拦截所有请求
registry.addinterceptor(new myinterceptor())
.addpathpatterns("/**");
}
}1.2 注册拦截器
在 springmvc 的配置类中,通过实现 webmvcconfigurer 接口并重写 addinterceptors 方法进行拦截器的注册。
第1步:创建配置类,并实现
webmvcconfigurer第2步:注册拦截器,重写
addinterceptors
@configuration
public class interceptorconfig implements webmvcconfigurer {
@override
public void addinterceptors(interceptorregistry registry) {
//1.拦截所有请求
registry.addinterceptor(new myinterceptor()).addpathpatterns("/v1/**");
}
}1.2.1 拦截器配置方案
1.拦截全部请求
registry.addinterceptor(new myinterceptor()) .addpathpatterns("/**");2.拦截指定请求
示例: 拦截所有 /v1/user/ 开头的请求,使用 /v1/user/**
registry.addinterceptor(new myinterceptor()) .addpathpatterns("/v1/user/**");3.排除指定请求
示例: 拦截所有的 /v1/user/ 开头的请求, 但是不包含登录请求 /v1/user/login
registry
.addinterceptor(new myinterceptor())
.addpathpatterns("/v1/user/**")
.excludepathpatterns("/v1/user/login");实战
限定访问时间拦截器
要求: 项目工程中,限定所有请求只能在每天的: 06:00 - 22:59 之间进行访问;
第1步:创建拦截器的类并实现拦截器功能;
public class timeaccessinterceptor implements handlerinterceptor {
@override
public boolean prehandle(httpservletrequest request,
httpservletresponse response, object handler) throws exception {
system.out.println("进入访问时间限定拦截器");
localtime now = localtime.now();//jdk中的时间对象
int hour = now.gethour();//获取当前时间对应小时
if (hour<6 || hour>22){
throw new runtimeexception("请在06:00~22:00之间进行访问");
}
return true;
}
}第2步:注册拦截器并添加拦截策略;
@configuration
public class interceptorconfig implements webmvcconfigurer {
@override
public void addinterceptors(interceptorregistry registry) {
registry.addinterceptor(new timeaccessinterceptor())
.addpathpatterns("/v1/user/**");
}
}第3步:重启工程测

权限校验拦截器
由后端进⾏⽤⼾⾝份验证,若不存在token或失效,则失败返回
采⽤拦截器来完成校验token的合法性
第1步:创建拦截器的类并实现拦截器功能;
@slf4j
@component
public class logininterceptor implements handlerinterceptor {
@override
public boolean prehandle(httpservletrequest request,
httpservletresponse response, object handler) throws exception {
//从 header 中获取 token
string jwttoken = request.getheader("user_token");
log.info("获取路径:{}", request.getrequesturi());
log.info("从header中获取token:{}", jwttoken);
//验证用户 token
claims claims = jwtutil.parsejwt(jwttoken);
if (null == claims){
response.setstatus(401);
return false;
}
log.info("令牌验证通过, 放行");
return true;
}
}第2步:注册拦截器并添加拦截策略;
@configuration
public class appconfig implements webmvcconfigurer {
@autowired
private logininterceptor logininterceptor;
private final list<string> excludes = arrays.aslist(
"/**/*.html",
"/css/**",
"/js/**",
"/pic/**",
"/*.jpg",
"/favicon.ico",
"/**/login",
"/register",
"/verification-code/send",
"/winning-records/show"
);
@override
public void addinterceptors(interceptorregistry registry) {
registry.addinterceptor(logininterceptor)
.addpathpatterns("/**")
.excludepathpatterns(excludes);
}
}限流功能拦截器[扩展]
因为系统处理能力有限,所以现在要对接口访问进行限流,实现方案是编写一个拦截器,对请求进行拦截和计算,要求: 单个ip限制在每分钟访问网站不能超过3次,超过3次则认为该客户端存在攻击风险,具体代码如下:
前提: 获取客户端的ip地址 request.getremoteaddr();
计数的hashmap:
{"192.168.1.11": 2, "192.168.1.12": 1, "192.168.1.13": 3}
记录时间的hashmap:
{"192.168.1.11": "2025-08-11 10:00:00", "192.168.1.12": "2025-08-11 11:00:00", "192.168.1.13": "2025-08-11 09:00:00"}
情况1: 该ip第一次访问网站;
情况2: 该ip之前访问过,但是距离本次访问的时间已经超过了60秒;
情况3: 该ip在1分钟之内访问过;
获取到客户端的ip地址: request.getremoteaddr();
存储计数的hashmap: {"192.168.1.11": 2, "192.168.1.12": 3, "192.168.1.13": 1}
存储最后一次访问时间的hashmap: {"192.168.1.11": "2025-07-11 00:00:00", "192.168.1.12":"2025-07-10:00:00:00", "192.168.1.13": "2020-01-01 00:00:00"}
第1步: 编写拦截器
package cn.tedu._03vehicle.base.interceptors; import org.springframework.web.servlet.handlerinterceptor; import jakarta.servlet.http.httpservletrequest; import jakarta.servlet.http.httpservletresponse; import java.time.localdatetime; import java.util.concurrent.concurrenthashmap; public class ratelimitinterceptor implements handlerinterceptor { private static final int max_requests_per_minute = 3; private static final long window_size_seconds = 60; // {"192.168.1.11": 1, "192.168.1.12": 1, "192.168.1.13": 2} private final concurrenthashmap<string, integer> clientcounts = new concurrenthashmap<>(); // {"192.168.1.11": "2024-01-01 14:00:00", "192.168.1.12": "2024-01-01 14:30:30", ...} private final concurrenthashmap<string, localdatetime> lastresettimes = new concurrenthashmap<>(); @override public boolean prehandle(httpservletrequest request, httpservletresponse response, object handler) throws exception { //获取客户端的ip地址 string clientid = request.getremoteaddr(); // 尝试获取上次重置时间 localdatetime lastresettime = lastresettimes.get(clientid); // 如果上次重置时间不存在或已过期,则重置计数器和时间 if (lastresettime == null || localdatetime.now().isafter(lastresettime.plusseconds(window_size_seconds))) { clientcounts.put(clientid, 0); lastresettimes.put(clientid, localdatetime.now()); } // 增加请求次数 integer count = clientcounts.get(clientid); count = count + 1; clientcounts.put(clientid, count); // 检查是否超过限制 if (count > max_requests_per_minute) { throw new runtimeexception("访问太频繁了"); } return true; } }第2步: 配置拦截器
@configuration
public class interceptorconfig implements webmvcconfigurer {
@override
public void addinterceptors(interceptorregistry registry) {
registry.addinterceptor(new ratelimitinterceptor())
.addpathpatterns("/v1/user/**");
}
}第3步: 重启工程测试
总结
到此这篇关于spring mvc拦截器的快速应用的文章就介绍到这了,更多相关spring mvc拦截器应用内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论