欢迎访问悦橙教程(wld5.com),关注java教程。悦橙教程  java问答|  每日更新
页面导航 : > > 文章正文

SpringBoot拦截器,它是Spring框架

来源: javaer 分享于  点击 28048 次 点评:244

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;
    }
}

代码中的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方法,该方法用于注册拦截器。在这里,通过调用InterceptorRegistryaddInterceptor方法来添加拦截器,并设置拦截的路径和排除的路径。

具体地,通过调用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之前进行相应的业务处理,执行的流程如下图

  1. 添加拦截器后,执行Controller的方法之前,请求会先被拦截器拦截住。
  2. 执行preHandle()方法,这个方法需要返回一个布尔类型的值。
  3. 如果返回true,就表示放行本次操作, 继续访问controller中的方法。
  4. 如果返回false,则不会放行(controller中的方法也不会执行)。
  5. controller当中的方法执行完毕后,再回过来执行postHandle()这个方法以及afterCompletion()方法。
  6. 执行完毕之后,最终给浏览器响应数据。

五、实际案例

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(): 添加的是映射后的真实路径,映射的真实路径末尾必须加/,不然映射不到,/适用于windowslinux

代码:

@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 过滤器和拦截器的区别

  1. 过滤器和拦截器触发时机不一样,过滤器是在请求进入容器后,但请求进入servlet之前进行预处理的。请求结束返回也是,是在servlet处理完后,返回给前端之前。
  2. 拦截器可以获取IOC容器中的各个bean,而过滤器就不行,因为拦截器是spring提供并管理的,spring的功能可以被拦截器使用,在拦截器里注入一个service,可以调用业务逻辑。而过滤器是JavaEE标准,只需依赖servlet api,不需要依赖spring
  3. 过滤器的实现基于回调函数。而拦截器(代理模式)的实现基于反射
  4. Filter是依赖于Servlet容器,属于Servlet规范的一部分,而拦截器则是独立存在的,可以在任何情况下使用。
  5. Filter的执行由Servlet容器回调完成,而拦截器通常通过动态代理(反射)的方式来执行。
  6. Filter的生命周期由Servlet容器管理,而拦截器则可以通过IoC容器来管理,因此可以通过注入等方式来获取其他Bean的实例,因此使用会更方便。

过滤器和拦截器非常相似,但是它们有很大的区别

最简单明了的区别就是

  1. 过滤器可以修改request,而拦截器不能。过滤器需要在servlet容器中实现,拦截器可以适用于javaEEjavaSE等各种环境
  2. 拦截器可以调用IOC容器中的各种依赖,而过滤器不能
  3. 过滤器只能在请求的前后使用,而拦截器可以详细到每个方法
相关栏目:

用户点评