SpringBoot很实用的请求过滤器 - FilterRegistrationBean

1、为什么需要Filter

在日常的开发中,我们的项目可能会被各种各样的客户端进行访问,那么,一些带有意图的朋友,就会利用自己所学的技术进行有目的的访问,那么我们的服务端就不再安全和可靠,我相信每位开发者都知道爬虫这种东西,那么当我们的请求不再安全,那么我们后台的数据就会变得透明。
数据透明,是一件多么可怕的事情,在这个数字潮流时代,数据就是金钱,在生活中任何一个系统都会录入我们的个人信息。
那么对请求进行过滤、请求的校验就变得尤为重要。

2、常用的Filter方式

  1. 在很久以前的Servlet项目中,可以使用@WebFilter注解来进行Filter的配置。
  2. 在目前SpringBoot作为后端主流框架而言,使用更多的是配置FilterRegistrationBean类,本文也主要以此类来配置Filter。

两种方式都是针对于Filter接口的实现类而言的。

3、Filter接口

一般我们实现Filter接口,只需要实现doFilter方法即可,但是也可以实现另外两个方法。

public interface Filter {
    default void init(FilterConfig filterConfig) throws ServletException {
    }

    void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;

    default void destroy() {
    }
}

4、FilterRegistrationBean类

可以看到此类的内部需要一个T类型的filter属性,而这个属性也是FilterRegistrationBean的核心,后面我们只需要将自定义的Filter放入到不同的FilterRegistrationBean中就可以了。

public class FilterRegistrationBean<T extends Filter> extends AbstractFilterRegistrationBean<T> {
    private T filter;

    public FilterRegistrationBean() {
        super(new ServletRegistrationBean[0]);
    }

    public FilterRegistrationBean(T filter, ServletRegistrationBean<?>... servletRegistrationBeans) {
        super(servletRegistrationBeans);
        Assert.notNull(filter, "Filter must not be null");
        this.filter = filter;
    }

    public T getFilter() {
        return this.filter;
    }

    public void setFilter(T filter) {
        Assert.notNull(filter, "Filter must not be null");
        this.filter = filter;
    }
}

5、自定义Filter代码实现

5.1、自定义Filter

自定义的Filter不用使用@Bean进行注入

5.1.1、UserFilter拦截对用户信息的请求

public class UserFilterConfig implements Filter {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("用户过滤器触发成功");
        // 核心代码省略
        filterChain.doFilter(servletRequest,servletResponse);
    }

}

5.1.2、AuthFilter拦截基本的认证信息

public class AuthFilterConfig implements Filter {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("认证过滤器触发成功");
        // 核心代码省略
        filterChain.doFilter(servletRequest,servletResponse);
    }
}

5.2、配置FilterRegistrationBean类

对于不同的Filter对象需要配置不同的FilterRegistrationBean类,因为存在重复代码,所以我进行了代码提取,并且向容器中注入相应的对象。
在此配置类中我使用到了Builder这种方式来进行数据的配置,这种方式在当前的SpringBoot框架中是非常常见的,这种方式也非常的好用,值得学习。

@Configuration
public class FilterConfig {

    @Bean
    public FilterRegistrationBean<UserFilterConfig> userFilterConfigFilterRegistrationBean(){
        FilterRegistrationBean<UserFilterConfig> userFilter = new FilterRegistrationBean<>();
        Builder<UserFilterConfig> userBuilder = new Builder<>(userFilter);
        userBuilder.filterConfiguration(UserFilterConfig.class,1,false,"/*");
        return userFilter;
    }

    @Bean
    public FilterRegistrationBean<AuthFilterConfig> authFilterConfigFilterRegistrationBean(){
        FilterRegistrationBean<AuthFilterConfig> authFilter = new FilterRegistrationBean<>();
        Builder<AuthFilterConfig> authBuilder = new Builder<>(authFilter);
        authBuilder.filterConfiguration(AuthFilterConfig.class,6,false,"/test/*");
        return authFilter;
    }


    private class Builder<T extends Filter>{

        private FilterRegistrationBean<T> filterRegistrationBean = null;

        public Builder(FilterRegistrationBean<T> filterRegistrationBean){
            this.filterRegistrationBean = filterRegistrationBean;
        }

        public Builder filterConfiguration(Class<? extends Filter> clazz,int order,boolean async,String ...patterns){
            T filter = null;
            try {
                filter = (T)clazz.getDeclaredConstructor().newInstance();
            } catch (Exception e) {
                System.out.println("[ " + clazz.toString() + " ] 过滤器对象不存在");
            }
            this.filterRegistrationBean.setFilter(filter); // 设置过滤器
            this.filterRegistrationBean.setOrder(order); // 设置启动顺序
            String clazzPath = clazz.toString().toLowerCase(Locale.ROOT);
            // 配置过滤器的名称,首字母一定要小写,不然拦截了请求后会报错
            this.filterRegistrationBean.setName(clazzPath.substring(clazzPath.lastIndexOf(".")));
            this.filterRegistrationBean.addUrlPatterns(patterns); // 配置拦截的请求地址
            return this;
        }

    }
}

6、运行结果

6.1、Controller类如下:

@RestController
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class FilterDemoController {

    private final ApplicationContext applicationContext;

    @GetMapping(value = "/abc")
    public void show(){
        for (String beanDefinitionName : applicationContext.getBeanDefinitionNames()) {
            if(beanDefinitionName.contains("Filter")){
                System.out.println(beanDefinitionName);
            }
        }
        System.out.println("=====> end <=====");
    }

    @GetMapping(value = "/test/abc")
    public void test(){

    }
}

/abc:会打印当前容器中所有的Filter对象。
/test/abc:什么也不做。

6.2、控制台显示

当我访问http://localhost:8080/abc时,就会触发UserFilter这个过滤器,结果如下:
在这里插入图片描述

可以看到,过滤器会先触发,然后打印出所有的Filter,容器中会存在两个不同的FilterRegistrationBean。

当我访问http://localhost:8080/test/abc时,就会触发AuthFilter这个过滤器,结果如下:
在这里插入图片描述

耶??为啥结果不是想象的那样??
这是因为我的UserFilter的拦截路径为/*,而AuthFilter的拦截路径为/test/*
那为什么UserFilter会在AuthFilter之前执行呢?
因为/*的拦截范围比/test/*的范围大,可以说/test/*是经过了/*拦截过再进行了匹配拦截。于此同时,我在相应的FilterRegistrationBean中也设置了Filter的执行顺序。

7、总结

  1. 使用Builder这种方式对配置类中的数据进行配置,是当前许多框架都在使用的方式,能够在一定程度上隐藏内部的实现。
  2. FilterRegistrationBean类提供了自定义FIlter的执行顺序,上文的Demo中因为拦截的范围问题,所以不容易看出存在执行顺序的问题,但是想要看到顺序问题也非常的简单,重新给setOrder方法赋值就行了,优先级低的先执行
  • 14
    点赞
  • 63
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot 中,可以通过实现 Filter 接口来自定义过滤器。在过滤器中,可以通过 HttpServletRequest 对象获取请求的信息。 下面是一个示例代码: ```java public class MyFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { // 初始化方法 } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; String method = request.getMethod(); // 获取请求方法(GET、POST等) String uri = request.getRequestURI(); // 获取请求路径 // 进行相应的逻辑处理 filterChain.doFilter(servletRequest, servletResponse); } @Override public void destroy() { // 销毁方法 } } ``` 在 doFilter 方法中,通过强制类型转换将 ServletRequest 对象转换为 HttpServletRequest 对象,然后就可以获取请求的信息了。例如,可以通过 request.getMethod() 方法获取请求的方法(GET、POST等),通过 request.getRequestURI() 方法获取请求的路径。 需要注意的是,如果在过滤器中对请求进行了处理,那么需要调用 filterChain.doFilter 方法将请求传递给下一个过滤器或 Servlet。如果没有调用该方法,请求将无法继续处理。 在 Spring Boot 中,可以通过配置 FilterRegistrationBean 来注册过滤器。例如: ```java @Configuration public class WebConfig { @Bean public FilterRegistrationBean<MyFilter> myFilter() { FilterRegistrationBean<MyFilter> registration = new FilterRegistrationBean<>(); registration.setFilter(new MyFilter()); registration.addUrlPatterns("/*"); registration.setName("MyFilter"); return registration; } } ``` 这里注册了一个名为 MyFilter过滤器,并将其应用于所有请求路径。当请求到达时,MyFilter 的 doFilter 方法将被调用,可以在其中获取请求信息等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值