当前位置: 首页 > article >正文

spring-security原理与应用系列:核心过滤器

目录

运行机制

WebSecurity

SecurityFilterChain

SecurityFilterChains

FilterChainProxy

VirtualFilterChain

内部结构

类图

FilterChainProxy

FilterChain

VirtualFilterChain

小结


        紧接上一篇文章,这一篇我们来看看FilterChainProxy类的运行机制及内部结构。

        FilterChainProxy是spring-security框架的核心过滤器,服务启动过程中,系统会根据我们自定义的配置类内容生成FilterChainProxy的对象;服务接收到HTTP请求后,就会将请求转发到该过滤器对象进行处理。

运行机制

        前面的篇章里,我们看到FilterChainProxy对象是建造者WebSecurity在构建步骤的最后一步里创建的。

        找到WebSecurity的performBuild()方法,如下所示:

WebSecurity

@Override
protected Filter performBuild() throws Exception {
   List<SecurityFilterChain> securityFilterChains = new ArrayList<SecurityFilterChain>(
         chainSize);
   for (RequestMatcher ignoredRequest : ignoredRequests) {
      securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
   }
   for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {
      securityFilterChains.add(securityFilterChainBuilder.build());
   }
   FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
   if (httpFirewall != null) {
      filterChainProxy.setFirewall(httpFirewall);
   }
   filterChainProxy.afterPropertiesSet();
   Filter result = filterChainProxy;
   postBuildAction.run();
   return result;
}

        在这里,创建FilterChainProxy对象时,输入了类型为List<SecurityFilterChain>的构造参数securityFilterChains对象,这个参数对象是如何进行赋值的,我们后续探究。我们先来看看SecurityFilterChain这个类。

        点击SecurityFilterChain,如下所示:

SecurityFilterChain

public interface SecurityFilterChain {
   boolean matches(HttpServletRequest request);
   List<Filter> getFilters();
}

        在这里,大概可以猜测,SecurityFilterChain的对象会保存待过滤的请求地址及对应的Filter对象。matches()方法用来判断用户请求与配置的请求地址是否匹配,getFilters()方法用来返回对应的过滤器对象。

        我们先运行一下程序看看。

        假设自定义配置如下:

SecurityFilterChains

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override

public void configure(WebSecurity web) {
        web.ignoring()
           .antMatchers("/css/**", "/js/**")
           .regexMatchers("/api/public/.*");
     }

}

        在这里,先进行简单的配置,即设置"/css/**"、 "/js/**"和"/api/public/.*"为不需要过滤的请求地址。

        设置断点,如下所示: 

        这里,可以看到每定义一个请求地址,就会有一个DefaultSecurityFilterChain对象。接下来看看FilterChainProxy对象是如何执行请求过滤的。

        点击FilterChainProxy,如下所示:

FilterChainProxy

        由于FilterChainProxy实现了Filter类,所以也是一个过滤器,过滤器一般都是执行doFilter()方法,如下所示:

public void doFilter(ServletRequest request, ServletResponse response,
      FilterChain chain) throws IOException, ServletException {
   boolean clearContext = request.getAttribute(FILTER_APPLIED) == null;
   if (clearContext) {
      try {
         request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
         doFilterInternal(request, response, chain);
      } finally {
         SecurityContextHolder.clearContext();
         request.removeAttribute(FILTER_APPLIED);
      }
   } else {
      doFilterInternal(request, response, chain);
   }
}

          点击doFilterInternal,如下所示:

private void doFilterInternal(ServletRequest request, ServletResponse response,
      FilterChain chain) throws IOException, ServletException {
   FirewalledRequest fwRequest = firewall
         .getFirewalledRequest((HttpServletRequest) request);
   HttpServletResponse fwResponse = firewall
         .getFirewalledResponse((HttpServletResponse) response);
   List<Filter> filters = getFilters(fwRequest);
   if (filters == null || filters.size() == 0) {
      fwRequest.reset();
      chain.doFilter(fwRequest, fwResponse);
      return;
   }
   VirtualFilterChain vfc = new VirtualFilterChain(fwRequest, chain, filters);
   vfc.doFilter(fwRequest, fwResponse);
}

         点击getFilters(),如下所示:

private List<Filter> getFilters(HttpServletRequest request) {
   for (SecurityFilterChain chain : filterChains) {
      if (chain.matches(request)) {
         return chain.getFilters();
      }
   }
   return null;
}

        在这里,将request与SecurityFilterChain 进行匹配,只要匹配到一个就返回这个SecurityFilterChain下的Filters,后续的SecurityFilterChain 会被忽略。

        接下来将Filters交由VirtualFilterChain进行处理,如下所示:

VirtualFilterChain

private static class VirtualFilterChain implements FilterChain {
   private final FilterChain originalChain;
   private final List<Filter> additionalFilters;
   private final FirewalledRequest firewalledRequest;
   private final int size;
   private int currentPosition = 0;
   private VirtualFilterChain(FirewalledRequest firewalledRequest,
         FilterChain chain, List<Filter> additionalFilters) {
      this.originalChain = chain;
      this.additionalFilters = additionalFilters;
      this.size = additionalFilters.size();
      this.firewalledRequest = firewalledRequest;
   }
   public void doFilter(ServletRequest request, ServletResponse response)
         throws IOException, ServletException {
      if (currentPosition == size) {
         this.firewalledRequest.reset();
         originalChain.doFilter(request, response);
      } else {
         currentPosition++;
         Filter nextFilter = additionalFilters.get(currentPosition - 1);
         nextFilter.doFilter(request, response, this);
      }
   }
}

        在这里,VirtualFilterChain会调用每个过滤的doFilter()方法进行处理。

内部结构

类图

FilterChainProxy

        FilterChainProxy实现了Filter接口,说明FilterChainProxy也是一个过滤器。但是FilterChainProxy不执行过滤任务,而是调用getFilters()方法,筛选出与当前请求匹配的多个过滤器,然后将这些过滤器直接交给VirtualFilterChain进行处理。

FilterChain

         这是一个过滤器链接口。

VirtualFilterChain

        这是一个过滤器链实现类。该类实例化时会从FilterChainProxy接收多个Filter对象。当外部调用了该VirtualFilterChain对象的doFilter()时,则该VirtualFilterChain对象会确保所有Filter对象的doFilter()方法都被调用。

        这里使用了职责链模式来实现这个功能。

小结

        FilterChainProxy是一个过滤器代理,自身不执行过滤逻辑,但会筛选出符合条件的过滤器,并将这些过滤器交给VirtualFilterChain过滤器链对象。

        VirtualFilterChain过滤器链对象不是过滤器,其通过职责链模式实现了将该链里的所有Filter按顺序执行下去。


http://www.kler.cn/a/614041.html

相关文章:

  • ARCGIS PRO SDK VB2022 图层要素类类型判断
  • WPF ContentTemplate
  • JavaScript网页设计高级案例:构建交互式图片画廊
  • 阀门流量控制系统MATLAB仿真PID
  • 上海做一场公关活动,有哪些媒体可以邀请?
  • 如何让 history 记录命令执行时间?Linux/macOS 终端时间戳设置指南
  • 蓝桥杯 数三角
  • 【Qt】程序加入开机自启动
  • 简单总结比较TCP,UDP,Socket协议
  • 05-SpringBoot3入门-整合SpringMVC(配置静态资源、拦截器)
  • HarmonyOS主题管理工具封装:动态切换、持久化存储与常见问题解析
  • Springboot 学习 之 Shardingsphere 按照日期水平分表(二)
  • 数据结构之多项式相加的链表实现
  • Spring Cloud ReactorServiceInstanceLoadBalancer 自定义负载均衡
  • BoomCut AI 技术创建本地化的营销视频
  • 质量工程师的2025:从“找bug“到“造质量“的职业进化
  • ​双目立体视觉的3D重建全流程
  • 《Linux运维实战:Ubuntu 22.04修改root用户默认名并禁止登录》
  • 自然语言处理|人工智能如何革新作文批改:技术全解析
  • notepad++ 正则表达式