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

spring-security原理与应用系列:securityFilterChainBuilders

目录

何时调用configure(HttpSecurity)

WebSecurityConfigurerAdapter

何时赋值securityFilterChainBuilders

WebSecurityConfigurerAdapter

WebSecurity

HttpSecurity建造者

类图

SecurityBuilder

AbstractSecurityBuilder

AbstractConfiguredSecurityBuilder

HttpSecurity

小结


      在前面的文章《spring-security原理与应用系列:核心过滤器》中,我们遗留了一个问题:创建FilterChainProxy对象时,输入了类型为List<SecurityFilterChain>的构造参数securityFilterChains对象,这个参数对象是如何进行赋值的?

       在文章《spring-security原理与应用系列:ignoredRequests》里,我们已经了解其中跟ignoredRequests相关的流程。

        这篇文章,我们来了解其中跟securityFilterChainBuilders对象相关的流程。

        设置断点,如下所示:

        在这里,我们看到securityFilterChainBuilders的类型是HttpSecurity。我们知道,自定义配置类里也使用到了HttpSecurity,如下所示:

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
   @Override
   protected void configure(HttpSecurity http) throws Exception {
      http.authorizeHttpRequests()
            .anyRequest().authenticated()
            .and()
            .formLogin(form -> form
                .loginPage("/myLogin.html")
                .permitAll())
            .logout(logout -> logout
                .permitAll())
            .csrf().disable();
        return http.build();
}

        相信其中一定有联系,所以接下来有两件事需要探究一下。

        其一:何时调用了自定义配置类里的configure(HttpSecurity http)方法?

        其二:何时赋值WebSecurity对象的securityFilterChainBuilders属性?

何时调用configure(HttpSecurity)

        点击WebSecurity建造者的对象构建的模板方法,如下所示:

AbstractConfiguredSecurityBuilder

@Override
protected final O doBuild() throws Exception {
   synchronized (configurers) {
      buildState = BuildState.INITIALIZING;
      beforeInit();
      init();
      buildState = BuildState.CONFIGURING;
      beforeConfigure();
      configure();
      buildState = BuildState.BUILDING;
      O result = performBuild();
      buildState = BuildState.BUILT;
      return result;
   }
}

        点击init()方法,如下所示:

private void init() throws Exception {
   Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
   for (SecurityConfigurer<O, B> configurer : configurers) {
      configurer.init((B) this);
   }
   for (SecurityConfigurer<O, B> configurer : configurersAddedInInitializing) {
      configurer.init((B) this);
   }
}

        点击configurer.init((B) this)方法,如下所示:

WebSecurityConfigurerAdapter

@Order(100)
public abstract class WebSecurityConfigurerAdapter implements WebSecurityConfigurer<WebSecurity> {

... ...

public void init(final WebSecurity web) throws Exception {
   final HttpSecurity http = getHttp();
   web.addSecurityFilterChainBuilder(http).postBuildAction(new Runnable() {
      public void run() {
         FilterSecurityInterceptor securityInterceptor = http
               .getSharedObject(FilterSecurityInterceptor.class);
         web.securityInterceptor(securityInterceptor);
      }
   });
}

        点击getHttp()调用,如下所示:

protected final HttpSecurity getHttp() throws Exception {
   if (http != null) {
      return http;
   }
   DefaultAuthenticationEventPublisher eventPublisher = objectPostProcessor
         .postProcess(new DefaultAuthenticationEventPublisher());
   localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);
   AuthenticationManager authenticationManager = authenticationManager();
   authenticationBuilder.parentAuthenticationManager(authenticationManager);
   Map<Class<? extends Object>, Object> sharedObjects = createSharedObjects();
   http = new HttpSecurity(objectPostProcessor, authenticationBuilder,
         sharedObjects);
   if (!disableDefaults) {
      // @formatter:off
      http
         .csrf().and()
         .addFilter(new WebAsyncManagerIntegrationFilter())
         .exceptionHandling().and()
         .headers().and()
         .sessionManagement().and()
         .securityContext().and()
         .requestCache().and()
         .anonymous().and()
         .servletApi().and()
         .apply(new DefaultLoginPageConfigurer<HttpSecurity>()).and()
         .logout();
      // @formatter:on
      ClassLoader classLoader = this.context.getClassLoader();
      List<AbstractHttpConfigurer> defaultHttpConfigurers =
            SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader);
      for(AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
         http.apply(configurer);
      }
   }
   configure(http);
   return http;
}

       在这里,我们终于找到答案了,我们看到了HttpSecurity对象的创建以及创建之后,调用了自定义配置类里的configure(HttpSecurity http)方法。

何时赋值securityFilterChainBuilders

        让我们回去前面的WebSecurityConfigurerAdapter的init()方法,如下所示:

WebSecurityConfigurerAdapter

@Order(100)
public abstract class WebSecurityConfigurerAdapter implements WebSecurityConfigurer<WebSecurity> {

... ...

public void init(final WebSecurity web) throws Exception {
   final HttpSecurity http = getHttp();
   web.addSecurityFilterChainBuilder(http).postBuildAction(new Runnable() {
      public void run() {
         FilterSecurityInterceptor securityInterceptor = http
               .getSharedObject(FilterSecurityInterceptor.class);
         web.securityInterceptor(securityInterceptor);
      }
   });
}

        在这里,调用getHttp()方法返回后赋值给http变量,紧接着调用了web.addSecurityFilterChainBuilder()方法。

        点击进入,如下所示:

WebSecurity

public WebSecurity addSecurityFilterChainBuilder(
      SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder) {
   this.securityFilterChainBuilders.add(securityFilterChainBuilder);
   return this;
}

        在这里,我们终于也找到了答案。即将上一步创建的HttpSecurity对象赋值给了WebSecurity对象的securityFilterChainBuilders属性。

HttpSecurity建造者

类图

       在这里,我们看到HttpSecurity跟WebSecurity一样,都实现了SecurityBuilder接口且继承了AbstractSecurityBuilder和AbstractConfiguredSecurityBuilder抽象类。

SecurityBuilder

        这个接口是建造者模式的顶级接口,含有建造者对外暴露的构建对象的一个接口方法 build() 。

        代码如下:

public interface SecurityBuilder<O> {
   O build() throws Exception;
}

AbstractSecurityBuilder

        这个类是SecurityBuilder接口的抽象子类,实现了接口的build()方法,为确保构建对象只被构建一次,对父接口方法build()进行了原子判断,从而保证每次只构建一次。另外,定义了一个抽象方法doBuild()供子类扩展。

AbstractConfiguredSecurityBuilder

        这个类实现了父类的doBuild()方法,这里是真正执行构建的地方。

        首先,使用了建造者模式在一个方法里定义了构建对象的所有步骤;

其次,使用了模板方法模式,将构建对象的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。

HttpSecurity

        实现了父类的performBuild()方法,这是构建对象的所有步骤的最后一个步。通过实现父类方法的方式来定义具体的执行内容。

@Override
protected DefaultSecurityFilterChain performBuild() throws Exception {
   Collections.sort(filters, comparator);
   return new DefaultSecurityFilterChain(requestMatcher, filters);
}

        在这里,可以看到HttpSecurity建造者的build()方法最终构建的是DefaultSecurityFilterChain对象,不过,该对象的两个构造参数requestMatcher和filters又是如何初始化的呢,我们后续再进行探究。

小结

        HttpSecurity与WebSecurity一样也是一个建造者。不过,WebSecurity建造的是核心过滤器,HttpSecurity建造的是SecurityFilterChain对象,且该SecurityFilterChain对象会作为核心过滤器的构造参数。

疏漏之处恭请雅正,良策佳议敬候惠示,凡所赐教必当铭感于心。

http://www.dtcms.com/a/100016.html

相关文章:

  • 职测-言语理解与表达
  • SD-WAN组网方案
  • pycharm相对路径引用方法
  • C++ 多态:面向对象编程的核心概念(一)
  • Android Product Flavors 深度解析与最佳实践:构建多版本应用的全方位指南
  • Redis的补充和RESP
  • 【工具】BioPred一个用于精准医疗中生物标志物分析的 R 软件包
  • C# StreamReader/StreamWriter 使用详解
  • 什么是 RBAC 权限模型?
  • jmeter web压力测试 压测
  • Android学习总结之算法篇三(打家劫舍)
  • 蓝桥杯—最小公倍数
  • Linux系统之dump命令的基本使用
  • Linux系统禁用swap
  • Xenium | 细胞邻域(Cellular Neighborhood)分析(fixed radius)
  • Spring AI MCP Server + Cline 快速搭建一个数据库 ChatBi 助手
  • QML编程中的性能优化二
  • C语言指针2
  • 2024蓝桥杯省赛C/C++大学B组 题解
  • [物联网iot]对比WIFI、MQTT、TCP、UDP通信协议
  • S32K144的SDK库中两种时钟初始化的区别(二)
  • MSTP和链路聚合
  • Nginx—nginx.conf 配置结构详解
  • Linux进程管理之进程间通信的相关知识(映射、管道(Pipe)通信、命名管道(FIFO)、消息队列、信号量、信号)
  • ctfshow WEB web8
  • 【Docker】Dockerfile 优化工具 hadolint
  • 普通人使用AI心得
  • 推挽振荡 ZVS 电路
  • RK3588S与RK3588S2差异说明
  • Java String 与 StringBuffer 深入解析:特性、实现与最佳实践