【拦截器Interceptor】springboot拦截器的使用和原理
- 【一】拦截器简介
- (1)简介
- 【2】作用
- 【二】实现步骤
- 【1】自定义拦截器,实现拦截器接口HandlerInterceptor
- 【2】将拦截器添加到容器当中
- 【3】配置拦截器的拦截规则
- 【4】拦截器的执行顺序
- 【三】拦截器参数
- 【1】获取请求头 request.getHeader
- 【2】Object handler 是什么参数
- 【3】ModelAndView modelAndView
- 【4】Exception ex
- 【5】HttpServletRequest request
- 【6】HttpServletResponse response
- 【四】多拦截器执行顺序
- 【五】拦截器和过滤器的区别
- 【六】拦截器的实际案例
- 【1】拦截器实现权限控制
- 【2】拦截器实现日志记录
- 【3】拦截器实现接口幂等性校验
- 【七】拦截器的性能优化和常见问题
- 【1】拦截器性能优化策略
- 【2】拦截器的常见问题和解决方案
- 【八】拦截器的原理和源码分析
【一】拦截器简介
(1)简介
拦截器就是用来拦截指定的请求,在请求前、请求处理后做一些响应的业务逻辑处理,或者在请求完成之后做一些资源释放。
拦截器最常用的使用场景就是认证,在请求开始之前,对当前请求进行权限校验,如果当前请求用户具备操作当前请求的权限,就对当前请求放行,允许执行业务逻辑;否则拦截当前请求,直接返回。
拦截器的功能通过网关也都是可以实现的,但是一些单体架构还是需要使用拦截器。
【2】作用
拦截器可以用于实现以下功能:
(1)权限控制:拦截器可以在请求到达处理器之前进行权限验证,从而实现对不同用户的访问控制。
(2)日志记录:拦截器可以在请求处理过程中记录请求和响应的详细信息,便于后期分析和调试。
(3)接口幂等性校验:拦截器可以在请求到达处理器之前进行幂等性校验,防止重复提交。
(4)数据校验:拦截器可以在请求到达处理器之前对请求数据进行校验,确保数据的合法性。
(5)缓存处理:拦截器可以在请求处理之后对响应数据进行缓存,提高系统性能。
【二】实现步骤
【1】自定义拦截器,实现拦截器接口HandlerInterceptor
要在SpringBoot中实现拦截器,首先需要创建一个类并实现HandlerInterceptor接口。HandlerInterceptor接口包含以下三个方法:
(1)preHandle:在请求到达处理器之前执行,可以用于权限验证、数据校验等操作。如果返回true,则继续执行后续操作;如果返回false,则中断请求处理。
(2)postHandle:在处理器处理请求之后执行,可以用于日志记录、缓存处理等操作。
(3)afterCompletion:在视图渲染之后执行,可以用于资源清理等操作。
/**
* 登录检查
* 1.配置到拦截器要拦截哪些请求
* 2.把这些配置放在容器中
*
* 实现HandlerInterceptor接口
*/
public class LoginInterceptor implements HandlerInterceptor {
/**
* 目标方法执行之前
* 登录检查写在这里,如果没有登录,就不执行目标方法
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 获取进过拦截器的路径
String requestURI = request.getRequestURI();
// 登录检查逻辑
HttpSession session = request.getSession();
Object loginUser = session.getAttribute("loginUser");
if(loginUser !=null){
// 放行
return true;
}
// 拦截 就是未登录,自动跳转到登录页面,然后写拦截住的逻辑
return false;
}
/**
* 目标方法执行完成以后
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
/**
* 页面渲染以后
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
【2】将拦截器添加到容器当中
要让拦截器生效,需要将其注册到InterceptorRegistry中。这可以通过实现WebMvcConfigurer接口并重写addInterceptors方法来实现。以下是一个简单的注册示例:
@Configuration
//定制SpringMVC的一些功能都使用WebMvcConfigurer
public class AdminWebConfig implements WebMvcConfigurer {
/**
* 配置拦截器
* @param registry 相当于拦截器的注册中心
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
//下面这句代码相当于添加一个拦截器 添加的拦截器就是我们刚刚创建的
registry.addInterceptor(new LoginInterceptor())
//addPathPatterns()配置我们要拦截哪些路径 addPathPatterns("/**")表示拦截所有请求,包括我们的静态资源
.addPathPatterns()
//excludePathPatterns()表示我们要放行哪些(表示不用经过拦截器)
//excludePathPatterns("/","/login")表示放行“/”与“/login”请求
//如果有静态资源的时候可以在这个地方放行
.excludePathPatterns("/","/login");
}
}
【3】配置拦截器的拦截规则
在注册拦截器时,可以通过addPathPatterns和excludePathPatterns方法来配置拦截器的拦截规则。addPathPatterns方法用于指定需要拦截的请求路径,excludePathPatterns方法用于指定不需要拦截的请求路径。以下是一个配置示例:
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/login", "/register");
}
}
在上述示例中,我们配置了拦截器拦截所有请求,但排除了登录和注册请求。
【4】拦截器的执行顺序
【三】拦截器参数
【1】获取请求头 request.getHeader
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String contenType = request.getHeader("Content-Type");
System.out.println("preHandle..."+contenType);
//放行
return true;
}
【2】Object handler 是什么参数
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String contenType = request.getHeader("Content-Type");
System.out.println(handler);
System.out.println("preHandle..."+contenType);
// 放行
return true;
}
使用PostMan发送请求后,控制台出现下面这个样子
这个参数有什么用?被调用的处理器对象,本质上是一个方法对象,对反射技术中的Method对象进行了再包装
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String contenType = request.getHeader("Content-Type");
// System.out.println(handler);
HandlerMethod hm = (HandlerMethod)handler;
// 通过hm.getMethod()就可以拿到原始执行的对象,拿到这个对象就可以进行反射
hm.getMethod();
System.out.println("preHandle..."+contenType);
// 放行
return true;
}
【3】ModelAndView modelAndView
封装了SpringMVC进行页面跳转的相关数据,但是我们现在都是返回JSON
【4】Exception ex
通过这个ex可以拿到原始的程序执行过程中出现的异常的
假设controller层抛了异常,在这里是可以拿到异常对象的,但是我们有异常处理机制,所以这里就没有那么大的需求了
如果处理器执行过程中出现异常对象,可以针对异常情况进行单独处理
【5】HttpServletRequest request
request:请求对象
【6】HttpServletResponse response
response:响应对象
【四】多拦截器执行顺序
当配置多个拦截器时,形成拦截器链
准备第二个拦截器
注册
@Configuration
//定制SpringMVC的一些功能都使用WebMvcConfigurer
public class AdminWebConfig implements WebMvcConfigurer {
/**
* 配置拦截器
* @param registry 相当于拦截器的注册中心
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 下面这句代码相当于添加一个拦截器 添加的拦截器就是我们刚刚创建的
registry.addInterceptor(new LoginInterceptor())
// addPathPatterns()配置我们要拦截哪些路径 addPathPatterns("/**")表示拦截所有请求,包括我们的静态资源
.addPathPatterns()
// excludePathPatterns()表示我们要放行哪些(表示不用经过拦截器)
// excludePathPatterns("/","/login")表示放行“/”与“/login”请求
// 如果有静态资源的时候可以在这个地方放行
.excludePathPatterns("/","/login");
// 第二个拦截器
registry.addInterceptor(new LoginInterceptor2()).addPathPatterns("/books");
}
}
那当我们配置了两个拦截器以后,会有一个执行顺序
拦截器链的运行顺序参照拦截器添加顺序为准,下面就是三个拦截器时的执行顺序
拦截器链的运行顺序参照拦截器添加顺序为准,下面就是三个拦截器时的执行顺序
【五】拦截器和过滤器的区别
拦截器和过滤器都可以实现对请求和响应的拦截和处理,但它们之间存在以下区别:
(1)执行顺序:过滤器在拦截器之前执行,拦截器在处理器之前执行。
(2)功能范围:过滤器可以对所有请求进行拦截,而拦截器只能对特定的请求进行拦截。
(3)生命周期:过滤器由Servlet容器管理,拦截器由Spring容器管理。
(4)使用场景:过滤器适用于对请求和响应的全局处理,拦截器适用于对特定请求的处理。
【六】拦截器的实际案例
【1】拦截器实现权限控制
拦截器可以在请求到达处理器之前进行权限验证,从而实现对不同用户的访问控制。以下是一个简单的权限控制示例:
public class AuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
HttpSession session = request.getSession();
User user = (User) session.getAttribute("user");
if (user == null) {
response.sendRedirect("/login");
return false;
}
return true;
}
}
在上述示例中,我们在preHandle方法中检查用户是否已登录,如果未登录,则重定向到登录页面并中断请求处理。
【2】拦截器实现日志记录
拦截器可以在请求处理过程中记录请求和响应的详细信息,便于后期分析和调试。以下是一个简单的日志记录示例:
public class LogInterceptor implements HandlerInterceptor {
private static final Logger logger = LoggerFactory.getLogger(LogInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
logger.info("Request URI: {}", request.getRequestURI());
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
logger.info("Response status: {}", response.getStatus());
}
}
在上述示例中,我们在preHandle方法中记录请求URI,在postHandle方法中记录响应状态。
【3】拦截器实现接口幂等性校验
拦截器可以在请求到达处理器之前进行幂等性校验,防止重复提交。以下是一个简单的幂等性校验示例:
public class IdempotentInterceptor implements HandlerInterceptor {
private static final String IDEMPOTENT_TOKEN = "idempotentToken";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String token = request.getHeader(IDEMPOTENT_TOKEN);
if (StringUtils.isEmpty(token)) {
throw new RuntimeException("Idempotent token is missing");
}
if (!checkIdempotentToken(token)) {
throw new RuntimeException("Duplicate request");
}
return true;
}
private boolean checkIdempotentToken(String token) {
// Check the token in the cache or database
// Return true if the token is valid, false otherwise
}
}
在上述示例中,我们在preHandle方法中检查请求头中的幂等性令牌,如果令牌无效,则抛出异常并中断请求处理。
【七】拦截器的性能优化和常见问题
【1】拦截器性能优化策略
拦截器在请求处理过程中可能会影响系统性能,以下是一些性能优化策略:
(1)减少拦截器数量:尽量将相关功能集中到一个拦截器中,避免创建过多的拦截器。
(2)精确配置拦截规则:通过addPathPatterns和excludePathPatterns方法精确配置拦截规则,避免不必要的拦截。
(3)使用异步处理:在拦截器中使用异步处理,避免阻塞请求处理过程。
(4)使用缓存:在拦截器中使用缓存,减少对数据库或其他资源的访问。
【2】拦截器的常见问题和解决方案
拦截器是一种用于处理请求和响应的中间件,它可以在请求到达目标处理器之前或响应返回客户端之前执行一些操作。然而,在实际使用过程中,我们可能会遇到一些问题,如拦截器不生效、执行顺序错误或影响性能等。接下来,我们将逐一分析这些问题的原因及解决方法。
(1)拦截器不生效:拦截器不生效的可能原因有很多,其中最常见的包括拦截器未注册到InterceptorRegistry、拦截规则配置错误等。为了解决这个问题,我们需要首先检查拦截器是否已经正确注册到InterceptorRegistry中,然后再检查拦截规则是否配置正确。如果发现问题,需要及时进行调整和修复。
(2)拦截器执行顺序错误:拦截器执行顺序错误的主要原因是拦截器的注册顺序错误。在实际应用中,拦截器的执行顺序是根据它们在InterceptorRegistry中的注册顺序来决定的。因此,为了解决这个问题,我们需要调整拦截器在InterceptorRegistry中的注册顺序,确保它们按照预期的顺序执行。
(3)拦截器影响性能:拦截器影响性能的主要原因是拦截器中的处理逻辑过于复杂或资源消耗过大。为了解决这个问题,我们需要对拦截器的处理逻辑进行优化,尽量减少不必要的计算和资源消耗。同时,我们还可以考虑使用一些性能监控工具,如JProfiler等,来对拦截器的性能进行实时监控和分析,从而找到性能瓶颈并进行优化。