0


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

1、为什么需要Filter

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

2、常用的Filter方式

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

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

3、Filter接口

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

publicinterfaceFilter{defaultvoidinit(FilterConfig filterConfig)throwsServletException{}voiddoFilter(ServletRequest var1,ServletResponse var2,FilterChain var3)throwsIOException,ServletException;defaultvoiddestroy(){}}

4、FilterRegistrationBean类

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

publicclassFilterRegistrationBean<TextendsFilter>extendsAbstractFilterRegistrationBean<T>{privateT filter;publicFilterRegistrationBean(){super(newServletRegistrationBean[0]);}publicFilterRegistrationBean(T filter,ServletRegistrationBean<?>... servletRegistrationBeans){super(servletRegistrationBeans);Assert.notNull(filter,"Filter must not be null");this.filter = filter;}publicTgetFilter(){returnthis.filter;}publicvoidsetFilter(T filter){Assert.notNull(filter,"Filter must not be null");this.filter = filter;}}

5、自定义Filter代码实现

5.1、自定义Filter

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

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

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

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

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

5.2、配置FilterRegistrationBean类

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

@ConfigurationpublicclassFilterConfig{@BeanpublicFilterRegistrationBean<UserFilterConfig>userFilterConfigFilterRegistrationBean(){FilterRegistrationBean<UserFilterConfig> userFilter =newFilterRegistrationBean<>();Builder<UserFilterConfig> userBuilder =newBuilder<>(userFilter);
        userBuilder.filterConfiguration(UserFilterConfig.class,1,false,"/*");return userFilter;}@BeanpublicFilterRegistrationBean<AuthFilterConfig>authFilterConfigFilterRegistrationBean(){FilterRegistrationBean<AuthFilterConfig> authFilter =newFilterRegistrationBean<>();Builder<AuthFilterConfig> authBuilder =newBuilder<>(authFilter);
        authBuilder.filterConfiguration(AuthFilterConfig.class,6,false,"/test/*");return authFilter;}privateclassBuilder<TextendsFilter>{privateFilterRegistrationBean<T> filterRegistrationBean =null;publicBuilder(FilterRegistrationBean<T> filterRegistrationBean){this.filterRegistrationBean = filterRegistrationBean;}publicBuilderfilterConfiguration(Class<?extendsFilter> 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);// 配置拦截的请求地址returnthis;}}}

6、运行结果

6.1、Controller类如下:

@RestController@RequiredArgsConstructor(onConstructor =@__(@Autowired))publicclassFilterDemoController{privatefinalApplicationContext applicationContext;@GetMapping(value ="/abc")publicvoidshow(){for(String beanDefinitionName : applicationContext.getBeanDefinitionNames()){if(beanDefinitionName.contains("Filter")){System.out.println(beanDefinitionName);}}System.out.println("=====> end <=====");}@GetMapping(value ="/test/abc")publicvoidtest(){}}

/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方法赋值就行了,优先级低的先执行
标签: spring boot java spring

本文转载自: https://blog.csdn.net/qq_45515182/article/details/127217852
版权归原作者 中国胖子风清扬 所有, 如有侵权,请联系我们删除。

“SpringBoot很实用的请求过滤器 - FilterRegistrationBean”的评论:

还没有评论