一、spring boot中拦截器是什么
在spring boot中,拦截器(interceptor)是一种基于aop(面向切面编程)思想的组件,用于在请求处理前后插入自定义逻辑,实现权限校验、日志记录、性能监控等非业务功能。
其核心作用是在不修改业务代码的前提下,对请求进行统一处理,类似于servlet中的filter,但更贴近spring mvc的体系。
二、拦截器与过滤器的区别

三、拦截器主要方法
在实现拦截器时,handlerinterceptor接口提供了三个主要方法,它们在请求处理的不同阶段发挥着重要作用。
prehandle
这个方法在请求进入controller之前被调用 。它的返回值是一个布尔类型,如果返回true,请求将继续被处理,进入controller。
如果返回false,请求将被拦截,后续的controller方法将不会被执行。在这个方法中,我们通常可以进行一些前置的处理操作,比如用户认证、权限校验、日志记录等。
posthandle
当controller方法执行完毕后,视图渲染之前,这个方法会被调用 。
在这个方法中,我们可以对modelandview进行一些操作,比如添加额外的模型数据,修改视图名称等。需要注意的是,如果在prehandle方法中返回了false,这个方法将不会被执行。
aftercompletion
在整个请求处理完成,包括视图渲染之后,这个方法会被调用。无论请求处理过程中是否发生异常,只要prehandle方法返回true,这个方法就会被执行。
我们可以在这个方法中进行一些资源清理、记录日志等收尾工作。如果请求处理过程中发生了异常,异常信息会通过 ex 参数传递进来,我们可以根据这个参数进行相应的异常处理。
@component
public class myinterceptor implements handlerinterceptor {
@override
public boolean prehandle(httpservletrequest request, httpservletresponse response, object handler) throws exception {
// 在请求处理之前执行的逻辑
system.out.println("prehandle被调用,请求即将进入controller");
return true; // 返回true表示放行请求,返回false则拦截请求
}
@override
public void posthandle(httpservletrequest request, httpservletresponse response, object handler, modelandview modelandview) throws exception {
// 在请求处理之后,视图渲染之前执行的逻辑
system.out.println("posthandle被调用,controller方法已执行完毕,视图即将渲染");
}
@override
public void aftercompletion(httpservletrequest request, httpservletresponse response, object handler, exception ex) throws exception {
// 在整个请求处理完成,包括视图渲染之后执行的逻辑
system.out.println("aftercompletion被调用,整个请求已处理完毕");
}
}@configuration
public class webmvcconfig1 implements webmvcconfigurer {
@override
public void addinterceptors(interceptorregistry registry) {
registry.addinterceptor(new myinterceptor())
.addpathpatterns("/**") // 拦截所有请求
.excludepathpatterns("/static/**"); // 排除/static目录下的静态资源请求
}
}四、5 种常见的拦截器使用场景
1.用户认证拦截器
用户认证拦截器的作用就是在用户请求进入 controller 之前,验证用户的登录状态。如果用户已经登录,允许请求继续处理。如果用户未登录,则返回错误信息或者重定向到登录页面。
@component
public class jwthandlerinterceptor implements handlerinterceptor {
@autowired
private jwttokenprovider jwttokenprovider;
@autowired
private userinfoservice userinfoservice;
@override
public boolean prehandle(httpservletrequest request, httpservletresponse response, object handler) throws exception {
if (!(handler instanceof handlermethod)) {
return true;
}
// 取出token
string token = request.getheader("token");
if (!jwttokenprovider.validatetoken(token)) {
response.setstatus(httpservletresponse.sc_unauthorized);
response.getwriter().write("{\"error\": \"token已失效,请重新登录\"}");
return false;
}
handlermethod handlermethod = (handlermethod) handler;
method method = handlermethod.getmethod();
// 判断方法上是否有noauth注解,如果有则跳过认证
if (method.isannotationpresent(noauth.class)) {
return true;
}
string username = jwttokenprovider.getusernamefromjwt(token);
user user = userinfoservice.getuserinfobyusername(username);
if (user == null) {
response.setstatus(httpservletresponse.sc_unauthorized);
response.getwriter().write("{\"error\": \"用户不存在\"}");
return false;
}
// 判断角色权限
hasroles hasroles = handlermethod.getmethodannotation(hasroles.class);
if (!objects.isnull(hasroles)) {
// 检查用户是否有所需角色
string[] roles = hasroles.value();
boolean hasrole = false;
// 角色校验 ...
if (!hasrole) {
response.setstatus(httpservletresponse.sc_forbidden);
response.getwriter().write("{\"error\": \"权限不足\"}");
return false;
}
}
// 将用户信息放入请求属性
request.setattribute("currentuser", user);
return true;
}
@override
public void posthandle(httpservletrequest request, httpservletresponse response, object handler, modelandview modelandview) throws exception {
handlerinterceptor.super.posthandle(request, response, handler, modelandview);
}
@override
public void aftercompletion(httpservletrequest request, httpservletresponse response, object handler, exception ex) throws exception {
handlerinterceptor.super.aftercompletion(request, response, handler, ex);
}
}在webmvcconfig配置类中,将jwthandlerinterceptor注册到spring的拦截器链中,并设置拦截路径为所有请求,排除了登录和注册接口,因为这两个接口不需要用户登录就可以访问。
@configuration
public class webmvcconfig implements webmvcconfigurer {
@override
public void addinterceptors(interceptorregistry registry) {
registry.addinterceptor(new jwthandlerinterceptor())
.addpathpatterns("/**")// 拦截所有请求
.excludepathpatterns("/login","/register");// 排除登录和注册接口
}
}通过这样的配置,就可以实现对用户登录状态的验证,确保只有登录用户才能访问受保护的资源。
2.日志记录拦截器
日志记录对于系统的运维和故障排查非常重要。日志记录拦截器可以在请求处理的前后记录请求的相关信息,比如请求的 url、请求方法、请求参数、客户端 ip 等。这些日志信息可以帮助我们了解系统的运行情况,分析用户行为,在出现问题时快速定位问题。
@component
public class requestlogginginterceptor implements handlerinterceptor {
private static final logger logger = loggerfactory.getlogger(requestlogginginterceptor.class);
@override
public boolean prehandle(httpservletrequest request, httpservletresponse response, object handler) throws exception {
logger.info("request url: {}, method: {}, ip: {}", request.getrequesturi(), request.getmethod(), request.getremoteaddr());
// 记录请求参数
map<string, string[]> parammap = request.getparametermap();
stringbuilder params = new stringbuilder();
if (!parammap.isempty()) {
for (map.entry<string, string[]> entry : parammap.entryset()) {
params.append(entry.getkey())
.append("=")
.append(string.join(",", entry.getvalue()))
.append("&");
}
if (params.length() > 0) {
params.deletecharat(params.length() - 1);
}
}
// 记录请求体(仅post/put/patch请求)
string method = request.getmethod();
string requestbody = "";
if (httpmethod.post.matches(method) ||
httpmethod.put.matches(method) ||
httpmethod.patch.matches(method)) {
// 使用包装请求对象来多次读取请求体
contentcachingrequestwrapper wrappedrequest =
new contentcachingrequestwrapper(request);
// 为了触发内容缓存,我们需要获取一次输入流
if (wrappedrequest.getcontentlength() > 0) {
wrappedrequest.getinputstream().read();
requestbody = new string(wrappedrequest.getcontentasbytearray(),
wrappedrequest.getcharacterencoding());
}
}
logger.info("request url: {}, method: {}, ip: {},params: {},requestbody: {}", request.getrequesturi(), request.getmethod(), request.getremoteaddr(), params, requestbody);
return true;
}
@override
public void aftercompletion(httpservletrequest request, httpservletresponse response, object handler, exception ex) throws exception {
logger.info("request to {} has been completed", request.getrequesturi());
if (ex != null) {
logger.error("an exception occurred during request handling", ex);
}
}
}3.性能监控拦截器
性能监控对于优化系统性能至关重要。性能监控拦截器可以用来计算和记录请求的处理时间,通过分析这些时间数据,我们可以找出系统中的性能瓶颈,进而进行针对性的优化。
@component
public class performanceinterceptor implements handlerinterceptor {
private static final threadlocal<long> starttimethreadlocal = new threadlocal<>();
@override
public boolean prehandle(httpservletrequest request, httpservletresponse response, object handler) throws exception {
long starttime = system.currenttimemillis();
starttimethreadlocal.set(starttime);
return true;
}
@override
public void posthandle(httpservletrequest request, httpservletresponse response, object handler, modelandview modelandview) throws exception {
// 计算请求处理时间
long endtime = system.currenttimemillis();
long starttime = starttimethreadlocal.get();
long executetime = endtime - starttime;
system.out.println("request url: " + request.getrequesturl() + ", execution time: " + executetime + "ms");
}
@override
public void aftercompletion(httpservletrequest request, httpservletresponse response, object handler, exception ex) throws exception {
starttimethreadlocal.remove();
}
}4.接口限流拦截器
在高并发场景下,接口限流是保护系统的重要手段 。接口限流拦截器可以限制单位时间内对某个接口的访问次数,防止因大量请求导致系统资源耗尽,从而保证系统的稳定性和可用性 。
@component
public class accesslimitinterceptor implements handlerinterceptor {
@autowired
private redistemplate<string, integer> redistemplate;
@override
public boolean prehandle(httpservletrequest request, httpservletresponse response, object handler) throws exception {
if (!(handler instanceof handlermethod)) {
return true;
}
handlermethod handlermethod = (handlermethod) handler;
accesslimit accesslimit = handlermethod.getmethodannotation(accesslimit.class);
if (accesslimit == null) {
return true;
}
int seconds = accesslimit.seconds();
int maxcount = accesslimit.maxcount();
boolean needlogin = accesslimit.needlogin();
if (needlogin) {
// 检查用户登录状态,这里省略具体实现
// 如果未登录,返回错误信息
return false;
}
string key = request.getremoteaddr() + request.getrequesturi();
integer count = redistemplate.opsforvalue().get(key);
if (count == null) {
redistemplate.opsforvalue().set(key, 1, seconds, timeunit.seconds);
} else if (count < maxcount) {
redistemplate.opsforvalue().increment(key, 1);
} else {
render(response, "请求过于频繁,请稍后再试");
return false;
}
return true;
}
private void render(httpservletresponse response, string msg) throws ioexception {
response.setcontenttype("application/json;charset=utf-8");
printwriter out = response.getwriter();
out.write(msg);
out.flush();
out.close();
}
@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 {
}
}5.数据加密解密拦截器
在数据传输和存储过程中,保护敏感数据的安全至关重要。数据加密解密拦截器可以在请求到达controller之前对敏感数据进行加密,在响应返回给客户端之前对加密数据进行解密,确保数据在传输和处理过程中的安全性。
比如在用户登录时,对用户输入的密码进行加密后再传输到服务器。在从数据库中查询用户的身份证号、手机号等敏感信息时,对查询结果进行解密后再返回给前端。
@component
public class encryptioninterceptor implements handlerinterceptor {
/**
* 密钥
*/
private static final string key = "secret_key";
@override
public boolean prehandle(httpservletrequest request, httpservletresponse response, object handler) throws exception {
// 假设请求参数中有一个名为"sensitivedata"的敏感数据需要加密
string sensitivedata = request.getparameter("sensitivedata");
if (sensitivedata != null) {
secretkey secretkey = new secretkeyspec(key.getbytes(), "aes");
byte[] encrypteddata = aesutil.encrypt(sensitivedata, secretkey);
string encrypteddatastr = base64.getencoder().encodetostring(encrypteddata);
// 将加密后的数据重新放回请求参数中
request.setattribute("sensitivedata", encrypteddatastr);
}
return true;
}
@override
public void aftercompletion(httpservletrequest request, httpservletresponse response, object handler, exception ex) throws exception {
// 假设响应中有一个名为"sensitiveresponsedata"的敏感数据需要解密
string sensitiveresponsedata = (string) request.getattribute("sensitiveresponsedata");
if (sensitiveresponsedata != null) {
secretkey secretkey = new secretkeyspec(key.getbytes(), "aes");
byte[] decodeddata = base64.getdecoder().decode(sensitiveresponsedata);
string decrypteddata = aesutil.decrypt(decodeddata, secretkey);
// 将解密后的数据重新放回请求属性中,方便后续处理
request.setattribute("sensitiveresponsedata", decrypteddata);
}
}
@override
public void posthandle(httpservletrequest request, httpservletresponse response, object handler, modelandview modelandview) throws exception {
handlerinterceptor.super.posthandle(request, response, handler, modelandview);
}
}五、总结
在 spring boot 开发中,拦截器就像是一把 “万能钥匙”,为我们提供了丰富的功能扩展和业务处理的可能性。
希望大家在实际项目中,能够根据业务需求,灵活地运用这些拦截器,让我们的 spring boot 应用更加健壮、高效、安全。
同时,拦截器还有很多值得深入探讨的地方,比如如何在拦截器中优雅地处理事务、如何实现更加复杂的动态拦截规则等 ,后续我们可以一起继续探索。
如果大家在使用拦截器的过程中有任何问题或心得,欢迎在留言区分享交流。
到此这篇关于spring boot 拦截器帮助解锁5大实用场景(推荐)的文章就介绍到这了,更多相关springboot拦截器内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论