Spring Security分析:SpringBoot DelegatingFilterProxy和FilterChainProxy自动配置

277 阅读2分钟

1. DelegatingFilterProxy和FilterChainProxy关系

在spring security官方文档中有这么一张图:

image.png

public DelegatingFilterProxy(String targetBeanName, @Nullable WebApplicationContext wac) {
 Assert.hasText(targetBeanName, "Target Filter bean name must not be null or empty");
 this.setTargetBeanName(targetBeanName);
 this.webApplicationContext = wac;
 if (wac != null) {
  this.setEnvironment(wac.getEnvironment());
 }
}

DelegatingFilterProxy将所有工作委托给实现 Filter 的 Spring Bean;在DelegatingFilterProxy初始化时会传入参数targetBeanName,即“springSecurityFilterChain” (由下文2.3可以看出springSecurityFilterChain就是FilterChainProxy的bean名称)。

2.自动配置过程

2.1 在SpringBoot的自动装配包下 org.springframework.boot.autoconfigure.security.servlet

@AutoConfiguration
@ConditionalOnClass({DefaultAuthenticationEventPublisher.class})
@EnableConfigurationProperties({SecurityProperties.class})
@Import({SpringBootWebSecurityConfiguration.class, SecurityDataConfiguration.class})
public class SecurityAutoConfiguration {
  public SecurityAutoConfiguration() {
  }

  @Bean
  @ConditionalOnMissingBean({AuthenticationEventPublisher.class})
  public DefaultAuthenticationEventPublisher authenticationEventPublisher(ApplicationEventPublisher publisher) {
    return new DefaultAuthenticationEventPublisher(publisher);
  }
}

可以看到其向ioc容器引入了SpringBootWebSecurityConfiguration配置类

@Configuration(
  proxyBeanMethods = false
)
@ConditionalOnMissingBean(
  name = {"springSecurityFilterChain"}
)
@ConditionalOnClass({EnableWebSecurity.class})
@EnableWebSecurity
static class WebSecurityEnablerConfiguration {
  WebSecurityEnablerConfiguration() {
  }
}

2.2 在SpringBootWebSecurityConfiguration配置类中,可以看到该配置类实例化WebSecurityEnablerConfiguration,该类上的注解@EnableWebSecurity

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import({ WebSecurityConfiguration.class, SpringWebMvcImportSelector.class, OAuth2ImportSelector.class,
  HttpSecurityConfiguration.class })
@EnableGlobalAuthentication
@Configuration
public @interface EnableWebSecurity {

 /**
  * Controls debugging support for Spring Security. Default is false.
  * @return if true, enables debug support with Spring Security
  */
 boolean debug() default false;

}

2.3 EnableWebSecurity配置类上导入了WebSecurityConfiguration


/**
 * Creates the Spring Security Filter Chain
 * @return the {@link Filter} that represents the security filter chain
 * @throws Exception
 */
@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public Filter springSecurityFilterChain() throws Exception {
 boolean hasConfigurers = this.webSecurityConfigurers != null && !this.webSecurityConfigurers.isEmpty();
 boolean hasFilterChain = !this.securityFilterChains.isEmpty();
 Assert.state(!(hasConfigurers && hasFilterChain),
   "Found WebSecurityConfigurerAdapter as well as SecurityFilterChain. Please select just one.");
 if (!hasConfigurers && !hasFilterChain) {
  WebSecurityConfigurerAdapter adapter = this.objectObjectPostProcessor
    .postProcess(new WebSecurityConfigurerAdapter() {
    });
  this.webSecurity.apply(adapter);
 }
 for (SecurityFilterChain securityFilterChain : this.securityFilterChains) {
  this.webSecurity.addSecurityFilterChainBuilder(() -> securityFilterChain);
  for (Filter filter : securityFilterChain.getFilters()) {
   if (filter instanceof FilterSecurityInterceptor) {
    this.webSecurity.securityInterceptor((FilterSecurityInterceptor) filter);
    break;
   }
  }
 }
 for (WebSecurityCustomizer customizer : this.webSecurityCustomizers) {
  customizer.customize(this.webSecurity);
 }
 return this.webSecurity.build();
}

image.png

@Override
protected Filter performBuild() throws Exception {
 Assert.state(!this.securityFilterChainBuilders.isEmpty(),
   () -> "At least one SecurityBuilder<? extends SecurityFilterChain> needs to be specified. "
     + "Typically this is done by exposing a SecurityFilterChain bean. "
     + "More advanced users can invoke " + WebSecurity.class.getSimpleName()
     + ".addSecurityFilterChainBuilder directly");
 int chainSize = this.ignoredRequests.size() + this.securityFilterChainBuilders.size();
 List<SecurityFilterChain> securityFilterChains = new ArrayList<>(chainSize);
 List<RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>>> requestMatcherPrivilegeEvaluatorsEntries = new ArrayList<>();
 for (RequestMatcher ignoredRequest : this.ignoredRequests) {
  WebSecurity.this.logger.warn("You are asking Spring Security to ignore " + ignoredRequest
    + ". This is not recommended -- please use permitAll via HttpSecurity#authorizeHttpRequests instead.");
  SecurityFilterChain securityFilterChain = new DefaultSecurityFilterChain(ignoredRequest);
  securityFilterChains.add(securityFilterChain);
  requestMatcherPrivilegeEvaluatorsEntries
    .add(getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain));
 }
 for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : this.securityFilterChainBuilders) {
  SecurityFilterChain securityFilterChain = securityFilterChainBuilder.build();
  securityFilterChains.add(securityFilterChain);
  requestMatcherPrivilegeEvaluatorsEntries
    .add(getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain));
 }
 if (this.privilegeEvaluator == null) {
  this.privilegeEvaluator = new RequestMatcherDelegatingWebInvocationPrivilegeEvaluator(
    requestMatcherPrivilegeEvaluatorsEntries);
 }
 FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
 if (this.httpFirewall != null) {
  filterChainProxy.setFirewall(this.httpFirewall);
 }
 if (this.requestRejectedHandler != null) {
  filterChainProxy.setRequestRejectedHandler(this.requestRejectedHandler);
 }
 filterChainProxy.afterPropertiesSet();

 Filter result = filterChainProxy;
 if (this.debugEnabled) {
  this.logger.warn("\n\n" + "********************************************************************\n"
    + "**********        Security debugging is enabled.       *************\n"
    + "**********    This may include sensitive information.  *************\n"
    + "**********      Do not use in a production system!     *************\n"
    + "********************************************************************\n\n");
  result = new DebugFilter(filterChainProxy);
 }
 this.postBuildAction.run();
 return result;
}

由springSecurityFilterChain该类注释可以看出其是创建Security Filter Chain的,会返回一个FilterChainProxy实例对象。 由这几个类的继承关系可知this.webSecurity.build()最终会执行 WebSecurity的performBuild方法,返回FilterChainProxy实例对象。