整合 Springboot 拦截器和过滤器的使用

3,271 阅读6分钟

Springboot

1:拦截器(Interceptor)

java中的拦截器是动态拦截action调用的对象。依赖于web框架,在springmvc中依赖于SpringMVC框架,在实现上基于Java的反射机制,属于AOP的一种应用,作用类似于过滤器,但是拦截器只能对Controller请求进行拦截,对其他的直接访问静态资源的请求无法拦截处理。

拦截器可以拦截前端请求,定义拦截器需要实现 HandlerInterceptor 接口,然后根据需求重写preHandle,postHandle,afterCompletion三个方法。

/**
 * 拦截器
 *
 * @program: myspringboot
 * @author: syt
 * @create: 2020-03-05 21:11
 */
@Component
@Slf4j
public class MyInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("前置拦截器****");
        //返回true表示通过请求,返回false表示请求被拦截
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("后置拦截器****");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("拦截器执行完成****");
    }
}

想要让自己的拦截器生效需要进行webmvc配置,自定义一个配置类实现WebMvcConfigurer接口,然后重写addInterceptors方法添加需要拦截的路径,JDK1.8以前都是继承于WebMvcConfigurerAdapter然后重新其中的方法,但是JDK1.8支持接口默认方法,官方就定义了可以不用继承WebMvcConfigurerAdapter直接实现WebMvcConfigurer即可。

**
 * 配置拦截器和过滤器
 *
 * @program: myspringboot
 * @author: syt
 * @create: 2020-03-05 21:16
 */
@Configuration
public class MyConfig implements WebMvcConfigurer {
    @Autowired
    private MyInterceptor myInterceptor;
    /**
     * 添加拦截器
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //addPa-thPatterns 用于添加拦截规则
        //excludePathPatterns 用于排除拦截
        /*要执行的拦截器*/
        registry.addInterceptor(myInterceptor).
                addPathPatterns("/**").
                excludePathPatterns("/**/login", "/**/esProductinfo/**");
    }

    /**
     * 解决跨域问题
     * 源(origin)就是协议、域名和端口号。
     * URL由协议、域名、端口和路径组成,如果两个URL的协议、域名和端口全部相同,
     * 则表示他们同源。否则,只要协议、域名、端口有任何一个不同,就是跨域
     *
     * @param registry
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/*")
                .allowedOrigins("*")
                .allowCredentials(true)
                .allowedMethods("GET", "POST", "DELETE", "PUT", "PATCH")
                .maxAge(3600);
    }


}

自定义自己的配置类也可以继承WebMvcConfigurationSupport类。这个类有许多默认的配置,但是如果继承了WebMvcConfigurationSupport这个类,SpringBoot的mvc自动装配就好失效,默认配置都需要自己定义,如静态文件地址 /**,需要重新addResourceHandlers方法添加静态文件地址

/**
 * 配置拦截器和过滤器
 *
 * @program: myspringboot
 * @author: syt
 * @create: 2020-03-05 21:16
 */
@Configuration
public class MyConfig extends WebMvcConfigurationSupport {
    @Autowired
    private MyInterceptor myInterceptor;

      /**
     * 静态资源
     *
     * @param registry
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
        registry.addResourceHandler("/static/**").addResourceLocations("classpath:/META-INF/resources/static/");
    }

    /**
     * 添加拦截器
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //addPa-thPatterns 用于添加拦截规则
        //excludePathPatterns 用于排除拦截
        /*要执行的拦截器*/
        registry.addInterceptor(myInterceptor).
                addPathPatterns("/**").
                excludePathPatterns("/**/login", "/**/esProductinfo/**");
    }

    /**
     * 解决跨域问题
     * 源(origin)就是协议、域名和端口号。
     * URL由协议、域名、端口和路径组成,如果两个URL的协议、域名和端口全部相同,
     * 则表示他们同源。否则,只要协议、域名、端口有任何一个不同,就是跨域
     *
     * @param registry
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/*")
                .allowedOrigins("*")
                .allowCredentials(true)
                .allowedMethods("GET", "POST", "DELETE", "PUT", "PATCH")
                .maxAge(3600);
    }
}

2:过滤器(filter)

方式一

对目标资源的请求和响应进行过滤截取。在请求到达servlet之前,进行逻辑判断,判断是否放行到servlet;也可以在一个响应response到达客户端之前进行过滤,判断是否允许返回客户端。

定义过滤器需要实现Filter接口

@Slf4j
public class MyFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("MyFilter的init方法");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        log.info("MyFilter的doFilter方法");
        //这里可以加一个判断,在过滤器请求不通过时可以返回自己的信息
        if (false) {
            filterChain.doFilter(servletRequest, servletResponse);
            return;
        } else {
            servletResponse.setContentType("text/html;charset=UTF-8;");
            PrintWriter out = servletResponse.getWriter();
            out.write("过滤器不通过");
            log.info("过滤器不通过");
            out.flush();
            out.close();
            return;
        }
    }

    @Override
    public void destroy() {
        log.info("MyFilter的destroy方法");
    }
}

想要让过滤器生效需要进行配置

@Configuration
public class MyConfig{
  
    /**
     * 添加filter
     *
     * @return
     */
    @Bean
    public FilterRegistrationBean filterRegistrationBean() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new MyFilter());
        //该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理
        registration.addInitParameter("targetFilterLifecycle", "true");
        registration.setEnabled(true);
        //filter的执行顺序,数字小先执行
        registration.setOrder(1);
        //需要拦截的url路径
        registration.addUrlPatterns("/demand/*", "/notice/*", "/query/*");
        return registration;
    }
}

方式二

可以直接用@WebFilter注解加@Component注解直接就可以创建过滤器了

@Slf4j
@Component
@WebFilter(filterName = "myFilter",urlPatterns = "/*")
public class MyFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("MyFilter的init方法");
    }

    /**
     * 这里是过滤请求的方法
     */
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        log.info("MyFilter的doFilter方法");
        if (false) {
            filterChain.doFilter(servletRequest, servletResponse);
            return;
        } else {
            servletResponse.setContentType("text/html;charset=UTF-8;");
            PrintWriter out = servletResponse.getWriter();
            out.write("过滤器不通过");
            log.info("过滤器不通过");
            out.flush();
            out.close();
            return;
        }
    }

    @Override
    public void destroy() {
        log.info("MyFilter的destroy方法");
    }
}

感觉拦截器和过滤器很像,都是可以拦截请求做相应的处理。接下来说下他俩的具体区别。

①:拦截器是基于java的反射机制,而过滤器基于函数回调。
②:过滤器依赖于servlet容器,拦截器不依赖于servlet容器。
③:拦截器只能对action请求起作用,而过滤器几乎对所有的请求都起作用。
④:拦截器可以访问action上下文,值栈里的对象,而过滤器不能。
⑤:在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次。
⑥:拦截器可以获取IOC容器中的各个bean,而过滤器就不行,(在拦截器里注入一个service,可以调用业务逻辑)。
⑦:过滤器是在请求进入容器后,但进入servlert前进行预处理的。响应请求也是,在servlet处理结束后,返回给客户端前触发。而拦截器提供了三个方法支持(1)preHandle:预处理回调方法,实现处理器的预处理(如登录检查),第三个参数为响应的处理器(如我们上一章的Controller实现); 返回值:true表示继续流程(如调用下一个拦截器或处理器);false表示流程中断(如登录检查失败),不会继续调用其他的拦截器或处理器,此时我们需要通过response来产生响应;postHandle:后处理回调方法,实现处理器的后处理(但在渲染视图之前),此时我们可以通过modelAndView(模型和视图对象)对模型数据进行处理或对视图进行处理,modelAndView也可能为null。 afterCompletion:整个请求处理完毕回调方法,即在视图渲染完毕时回调,如性能监控中我们可以在此记录结束时间并输出消耗时间,还可以进行一些资源清理,类似于try-catch-finally中的finally,但仅调用处理器执行链中preHandle返回true的拦截器的afterCompletion。

码云地址:gitee.com/sytsuccess/…

参考链接:blog.csdn.net/u013806366/…