SpringBoot拦截器,它是Spring框架
SpringBoot拦截器,它是Spring框架
一、概述
在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;
}
}
@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
的实例,因此使用会更方便。
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
容器中的各种依赖,而过滤器不能 - 过滤器只能在请求的前后使用,而拦截器可以详细到每个方法
用户点评