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

超、凢脫俗 2024-04-03 10:30 169阅读 0赞

1、为什么需要Filter

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

2、常用的Filter方式

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

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

3、Filter接口

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

  1. public interface Filter {
  2. default void init(FilterConfig filterConfig) throws ServletException {
  3. }
  4. void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;
  5. default void destroy() {
  6. }
  7. }

4、FilterRegistrationBean类

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

  1. public class FilterRegistrationBean<T extends Filter> extends AbstractFilterRegistrationBean<T> {
  2. private T filter;
  3. public FilterRegistrationBean() {
  4. super(new ServletRegistrationBean[0]);
  5. }
  6. public FilterRegistrationBean(T filter, ServletRegistrationBean<?>... servletRegistrationBeans) {
  7. super(servletRegistrationBeans);
  8. Assert.notNull(filter, "Filter must not be null");
  9. this.filter = filter;
  10. }
  11. public T getFilter() {
  12. return this.filter;
  13. }
  14. public void setFilter(T filter) {
  15. Assert.notNull(filter, "Filter must not be null");
  16. this.filter = filter;
  17. }
  18. }

5、自定义Filter代码实现

5.1、自定义Filter

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

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

  1. public class UserFilterConfig implements Filter {
  2. @Override
  3. public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
  4. System.out.println("用户过滤器触发成功");
  5. // 核心代码省略
  6. filterChain.doFilter(servletRequest,servletResponse);
  7. }
  8. }

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

  1. public class AuthFilterConfig implements Filter {
  2. @Override
  3. public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
  4. System.out.println("认证过滤器触发成功");
  5. // 核心代码省略
  6. filterChain.doFilter(servletRequest,servletResponse);
  7. }
  8. }

5.2、配置FilterRegistrationBean类

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

  1. @Configuration
  2. public class FilterConfig {
  3. @Bean
  4. public FilterRegistrationBean<UserFilterConfig> userFilterConfigFilterRegistrationBean(){
  5. FilterRegistrationBean<UserFilterConfig> userFilter = new FilterRegistrationBean<>();
  6. Builder<UserFilterConfig> userBuilder = new Builder<>(userFilter);
  7. userBuilder.filterConfiguration(UserFilterConfig.class,1,false,"/*");
  8. return userFilter;
  9. }
  10. @Bean
  11. public FilterRegistrationBean<AuthFilterConfig> authFilterConfigFilterRegistrationBean(){
  12. FilterRegistrationBean<AuthFilterConfig> authFilter = new FilterRegistrationBean<>();
  13. Builder<AuthFilterConfig> authBuilder = new Builder<>(authFilter);
  14. authBuilder.filterConfiguration(AuthFilterConfig.class,6,false,"/test/*");
  15. return authFilter;
  16. }
  17. private class Builder<T extends Filter>{
  18. private FilterRegistrationBean<T> filterRegistrationBean = null;
  19. public Builder(FilterRegistrationBean<T> filterRegistrationBean){
  20. this.filterRegistrationBean = filterRegistrationBean;
  21. }
  22. public Builder filterConfiguration(Class<? extends Filter> clazz,int order,boolean async,String ...patterns){
  23. T filter = null;
  24. try {
  25. filter = (T)clazz.getDeclaredConstructor().newInstance();
  26. } catch (Exception e) {
  27. System.out.println("[ " + clazz.toString() + " ] 过滤器对象不存在");
  28. }
  29. this.filterRegistrationBean.setFilter(filter); // 设置过滤器
  30. this.filterRegistrationBean.setOrder(order); // 设置启动顺序
  31. String clazzPath = clazz.toString().toLowerCase(Locale.ROOT);
  32. // 配置过滤器的名称,首字母一定要小写,不然拦截了请求后会报错
  33. this.filterRegistrationBean.setName(clazzPath.substring(clazzPath.lastIndexOf(".")));
  34. this.filterRegistrationBean.addUrlPatterns(patterns); // 配置拦截的请求地址
  35. return this;
  36. }
  37. }
  38. }

6、运行结果

6.1、Controller类如下:

  1. @RestController
  2. @RequiredArgsConstructor(onConstructor = @__(@Autowired))
  3. public class FilterDemoController {
  4. private final ApplicationContext applicationContext;
  5. @GetMapping(value = "/abc")
  6. public void show(){
  7. for (String beanDefinitionName : applicationContext.getBeanDefinitionNames()) {
  8. if(beanDefinitionName.contains("Filter")){
  9. System.out.println(beanDefinitionName);
  10. }
  11. }
  12. System.out.println("=====> end <=====");
  13. }
  14. @GetMapping(value = "/test/abc")
  15. public void test(){
  16. }
  17. }

/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方法赋值就行了,优先级低的先执行

发表评论

表情:
评论列表 (有 0 条评论,169人围观)

还没有评论,来说两句吧...

相关阅读