一、概述
在Spring Boot
中,拦截器是一种用于拦截和处理HTTP
请求的机制。它是Spring
框架提供的一种中间件,用于在请求到达控制器(Controller
)之前或之后执行一些共享的逻辑。
Spring Boot
的拦截器基于Spring MVC
框架中的HandlerInterceptor
接口实现。通过创建一个自定义的拦截器类并实现HandlerInterceptor
接口,可以定义拦截器要执行的逻辑和行为。
二、定义拦截器
在Spring Boot
中定义拦截器十分的简单,只需要创建一个拦截器类,并实现HandlerInterceptor
接口即可。
HandlerInterceptor
接口中定义以下3
个方法,如下表:
返回值类型 | 方法声明 | 描述 |
---|---|---|
boolean | preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) | 该方法在控制器处理请求方法前执行,其返回值表示是否中断后续操作,返回true表示继续向下执行,返回false表示中断后续操作。 |
void | postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) | 该方法在控制器处理请求方法调用之后、解析视图之前执行,可以通过此方法对请求域中的模型和视图做进一步修改。 |
void | afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) | 该方法在视图渲染结束后执行,可以通过此方法实现资源清理、记录日志信息等工作。 |
它接收三个参数:
- HttpServletRequest request: 表示当前的HTTP请求
- HttpServletResponse response: 表示当前的HTTP响应
- Object handler: 表示被拦截的处理器(一般是Controller中的方法)
三、使用拦截器
3.1 自定义拦截器
@Component
public class LoginInterceptor implements HandlerInterceptor {
//调用目标方法之前执行的方法
//如果返回ture表示拦截器验证成功,执行目标方法
//如果返回false表示拦截器验证失败,不再继续执行后续业务
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
//用户登录判断业务
HttpSession session = request.getSession(false);
if (session != null && session.getAttribute("session_userinfo") != null) {
//用户已登录
return true;
}
response.setStatus(401);
return false;
}
}
代码中的preHandle
方法是拦截器的主要方法,在目标方法调用之前执行。
在preHandle
方法中,首先通过request.getSession(false)
获取当前请求的HttpSession
对象(如果存在的话),然后判断该HttpSession
对象是否为null
并且是否存在名为”session_userinfo”的属性。如果这个条件成立,说明用户已经登录,可以继续执行后续的业务,于是返回true
,否则验证失败,将HTTP
响应的状态码设置为401
,表示未授权,然后返回false
,不再继续执行后续业务。
3.2 注册拦截器
@Configuration
public class MyConfig implements WebMvcConfigurer {
//注入
@Autowired
private LoginInterceptor loginInterceptor;
//将拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor)
.addPathPatterns("/**") //拦截所有的 url
.excludePathPatterns("/user/login")//排除url: /user/login (登录)
.excludePathPatterns("/user/reg") //排除url: /user/reg (注册)
.excludePathPatterns("/image/**")//排除 image(图像) 文件夹下的所有文件
.excludePathPatterns("/**/*.js")//排除任意深度目录下的所有".js"文件
.excludePathPatterns("/**/*.css");
}
}
UserController:
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping("/login")
public String login(){
return "login";
}
@RequestMapping("/index")
public String index(){
return "index";
}
@RequestMapping("/reg")
public String reg(){
return "reg";
}
}
在配置类中,重写了addInterceptors
方法,该方法用于注册拦截器。在这里,通过调用InterceptorRegistry
的addInterceptor
方法来添加拦截器,并设置拦截的路径和排除的路径。
具体地,通过调用addInterceptor(loginInterceptor)
来添加LoginInterceptor
拦截器。然后使用addPathPatterns
方法指定需要拦截的URL
路径模式,这里使用”/**”表示拦截所有的URL
。使用excludePathPatterns
方法来排除一些特定的URL
路径,这些路径不会被拦截。
“//*.js”,”“:表示零个或多个路径段(目录或文件夹),可以匹配任意深度的目录结构。
“/*.js”:表示以”.js”结尾的文件名。
常见的拦截配置:
拦截路径 | 含义 | 举例 |
---|---|---|
/* | 拦截一级路径 | 能拦截 /user,/login,不能拦截 /user/login |
/** | 拦截任意路径 | 拦截所有 |
/user/* | 拦截/user的下一级路径 | 能拦截 /user/getList,不能拦截 /user/getList/1 和 /user |
/user/** | 拦截/user下的所有路径 | 拦截/user下的所有路径,但是不能拦截 /book/getList |
此外拦截器不仅可以拦截项目中的URL
,还可以拦截静态资源(html
,图片等)。
四、执行流程
有了拦截器之后,会在调用Controller
之前进行相应的业务处理,执行的流程如下图
- 添加拦截器后,执行
Controller
的方法之前,请求会先被拦截器拦截住。 - 执行
preHandle()
方法,这个方法需要返回一个布尔类型的值。 - 如果返回
true
,就表示放行本次操作, 继续访问controller
中的方法。 - 如果返回
false
,则不会放行(controller
中的方法也不会执行)。 controller
当中的方法执行完毕后,再回过来执行postHandle()
这个方法以及afterCompletion()
方法。- 执行完毕之后,最终给浏览器响应数据。
五、实际案例
5.1 统一访问前缀添加
所有请求地址添加test
前缀:在WebMvcConfigurer
接口中,configurePathMatch
方法用于配置路径匹配规则。
@Configuration
public class MyConfig implements WebMvcConfigurer {
//统一访问前缀的添加
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.addPathPrefix("test", new Predicate<Class<?>>() {
@Override
public boolean test(Class<?> aClass) {
return true;
}
});
}
}
在这个例子中,传递给addPathPrefix
方法的前缀是”test”,而Predicate
对象是一个匿名内部类,实现了Predicate<Class<?>>
接口。Predicate
接口是Java 8
中引入的函数式接口,它的test方法用于判断传入的类是否符合条件。
在这个匿名内部类中,test方法被重写为总是返回true,这意味着所有的类都符合条件,都会被添加统一访问前缀。
因此,通过这段代码的配置,所有的请求路径都会在前面添加”test”前缀。例如,原始路径为”/example”,添加了前缀后的路径就变为”/test/example”。这样可以实现对请求路径的统一处理。
注意:如果加了前缀,拦截器的排除路径也要跟着改动:
//将拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor)
.addPathPatterns("/**") //拦截所有的 url
.excludePathPatterns("/**/user/login")//排除url: /user/login (登录)
.excludePathPatterns("/**/user/reg") //排除url: /user/reg (注册)
.excludePathPatterns("/**/image/**")//排除 image(图像) 文件夹下的所有文件
.excludePathPatterns("/**/*.js")//排除任意深度目录下的所有".js"文件
.excludePathPatterns("/**/*.css");
}
5.2 配置本地资源映射路径
实现WebMvcConfigurer
,重写addResourceHandlers(ResourceHandlerRegistry registry)
方法
- addResourceHandler():添加的是访问路径
- addResourceLocations(): 添加的是映射后的真实路径,映射的真实路径末尾必须加
/
,不然映射不到,/
适用于windows
和linux
代码:
@Configuration
public class MyWebMVCConfig implements WebMvcConfigurer {
@Value("${file.location}") // D:/test/
String fileLocation; // 这两个是路径
@Value("${file.path}") // /file/**
String filePath;
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
//匹配到resourceHandler,将URL映射至location,也就是本地文件夹
registry.addResourceHandler(filePath)
//这里最后一个/必须写
.addResourceLocations("file:///" + fileLocation);
}
}
这段代码意思就配置一个拦截器,如果访问路径是addResourceHandler
中的filePath
这个路径那么就映射到访问本地的addResourceLocations
的参数的这个路径上,这样就可以让别人访问服务器的本地文件了,比如本地图片或者本地音乐视频什么的。
六、应用场景
拦截器在SpringBoot
中扮演着多面手的角色,适用于多种场景,有助于增强应用程序的功能和安全性。以下是拦截器的一些典型应用:
-
身份验证与授权
使用拦截器检查用户的身份验证状态和权限级别,确保只有经过验证且有适当权限的用户能够访问特定资源。这为应用程序的安全性提供了第一道防线。 -
日志记录与审计:
在请求处理的不同阶段(如请求到达前、响应发送后)插入日志记录逻辑,帮助开发者跟踪系统行为、调试问题,并满足审计需求。 -
性能分析与监控:
拦截器可用于测量请求处理时间,收集性能指标,识别潜在的性能瓶颈,从而优化系统的响应速度和服务质量。 -
跨域资源共享(CORS)配置:
当构建RESTful API
时,通过拦截器设置适当的HTTP响应头来允许或限制跨域请求,简化前端与后端之间的交互。 -
异常捕捉与友好错误处理:
捕获请求处理过程中抛出的异常,提供统一的错误处理机制,确保最终用户接收到格式化良好且友好的错误信息,而不是直接暴露底层技术细节 -
数据预处理与后处理:
对进入的数据进行验证或转换,确保其符合预期格式;同时,在响应生成之后对输出数据进行格式化或加密等操作,以满足业务逻辑要求。 -
流量控制与限流策略:
实施流量控制措施,例如限制同一IP地址在一定时间内发起的请求数量,或者控制并发请求的数量,以保护系统免受过载影响。
通过合理运用拦截器,开发人员可以在不改变原有业务逻辑的前提下,轻松地向应用程序添加这些额外的功能,提高系统的灵活性、安全性和可维护性。
七、拓展
7.1 过滤器和拦截器的区别
- 过滤器和拦截器触发时机不一样,过滤器是在请求进入容器后,但请求进入
servlet
之前进行预处理的。请求结束返回也是,是在servlet
处理完后,返回给前端之前。 - 拦截器可以获取
IOC
容器中的各个bean
,而过滤器就不行,因为拦截器是spring
提供并管理的,spring
的功能可以被拦截器使用,在拦截器里注入一个service
,可以调用业务逻辑。而过滤器是JavaEE
标准,只需依赖servlet api
,不需要依赖spring
。 - 过滤器的实现基于回调函数。而拦截器(代理模式)的实现基于反射
Filter
是依赖于Servlet
容器,属于Servlet
规范的一部分,而拦截器则是独立存在的,可以在任何情况下使用。Filter
的执行由Servlet
容器回调完成,而拦截器通常通过动态代理(反射)的方式来执行。Filter
的生命周期由Servlet
容器管理,而拦截器则可以通过IoC
容器来管理,因此可以通过注入等方式来获取其他Bean
的实例,因此使用会更方便。
过滤器和拦截器非常相似,但是它们有很大的区别
最简单明了的区别就是
- 过滤器可以修改
request
,而拦截器不能。过滤器需要在servlet
容器中实现,拦截器可以适用于javaEE
,javaSE
等各种环境 - 拦截器可以调用
IOC
容器中的各种依赖,而过滤器不能 - 过滤器只能在请求的前后使用,而拦截器可以详细到每个方法
来源链接:https://www.cnblogs.com/ciel717/p/18806305
如有侵犯您的版权,请及时联系3500663466#qq.com(#换@),我们将第一时间删除本站数据。
暂无评论内容