前面对Spring Security 源码的讲解都比较零散,今天捋一遍Spring Security的初始化流程,顺便将前面的源码分析文章串起来。

1、SecurityAutoConfiguraion

分析一个SpringBoot的stater,都是从xxxAutoConfiguration类开始的。而Spring Security 的自动化配置类是SecurityAutoConfiguration,所以就从这个自动配置类开始分析:

  1. @Configuration(proxyBeanMethods = false)
  2. @ConditionalOnClass(DefaultAuthenticationEventPublisher.class)
  3. @EnableConfigurationProperties(SecurityProperties.class)
  4. @Import({ SpringBootWebSecurityConfiguration.class, WebSecurityEnablerConfiguration.class,
  5. SecurityDataConfiguration.class })
  6. public class SecurityAutoConfiguration {
  7. @Bean
  8. @ConditionalOnMissingBean(AuthenticationEventPublisher.class)
  9. public DefaultAuthenticationEventPublisher authenticationEventPublisher(ApplicationEventPublisher publisher) {
  10. return new DefaultAuthenticationEventPublisher(publisher);
  11. }
  12. }

这个Bean中,定义了一个事件发布器。另外导入了三个配置:

  1. SpringBootWebSecurityConfiguration这个配置的作用是如果开发者没有自定义WebSecurityConfigurerAdapter的话,这里提供一个默认的实现。
  2. WebSecurityEnablerConfiguration这个配置是Spring Security的核心配置,也将是我们分析的重点。
  3. SecurityDataConfiguration提供了Spring Security整合Spring Data的支持,由于国内使用mybatis较多,所以这个配置发光发热的场景有限。

    2、WebSecurityEnablerConfiguration

    ```java @Configuration(proxyBeanMethods = false) @ConditionalOnBean(WebSecurityConfigurerAdapter.class) @ConditionalOnMissingBean(name = BeanIds.SPRING_SECURITY_FILTER_CHAIN) @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) @EnableWebSecurity public class WebSecurityEnablerConfiguration {

}

  1. 这个配置没啥说的,给了一堆生效条件,最终给出一个`@EnableWebSecurity`注解,看来初始化的重任落在<br />`@EnableWebSecurity`注解身上。
  2. <a name="Kts17"></a>
  3. # 3、@EnableWebSecurity
  4. ```java
  5. @Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
  6. @Target(value = { java.lang.annotation.ElementType.TYPE })
  7. @Documented
  8. @Import({ WebSecurityConfiguration.class,
  9. SpringWebMvcImportSelector.class,
  10. OAuth2ImportSelector.class })
  11. @EnableGlobalAuthentication
  12. @Configuration
  13. public @interface EnableWebSecurity {
  14. /**
  15. * Controls debugging support for Spring Security. Default is false.
  16. * @return if true, enables debug support with Spring Security
  17. */
  18. boolean debug() default false;
  19. }

@EnableWebSecurity注解所做的事情,有两件比较重要:

  1. 导入WebSecurityConfiguration配置。
  2. 通过@EnableGlobalAuthentication注解引入全局配置。

    3.1、WebSecurityConfiguration

    1. public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
    2. }

    WebSecurityConfiguration实现了两个接口,其中ImportAware接口和@Import注解一起使用的。实现了ImportAware接口的配置类可以方便的通过setImportMetadata方法获取到导入类中的数据配置。有点绕,梳理下,就是WebSecurityConfiguration实现了ImportAware接口,使用@Import注解在@EnableWebSecurity上导入WebSecurityConfiguration之后,在WebSecurityConfigurationsetImportMetadata方法中可以方便的获取到@EnableWebSecurity中的属性,这里主要是debug属性。
    来看下WebSecurityConfiguration#setImportMetadata方法:

    1. public void setImportMetadata(AnnotationMetadata importMetadata) {
    2. Map<String, Object> enableWebSecurityAttrMap = importMetadata
    3. .getAnnotationAttributes(EnableWebSecurity.class.getName());
    4. AnnotationAttributes enableWebSecurityAttrs = AnnotationAttributes
    5. .fromMap(enableWebSecurityAttrMap);
    6. debugEnabled = enableWebSecurityAttrs.getBoolean("debug");
    7. if (webSecurity != null) {
    8. webSecurity.debug(debugEnabled);
    9. }
    10. }

    获取到debug属性赋值给webSecurity
    实现BeanClassLoaderAware接口则是为了方便的获取ClassLoader
    🎯在WebSecurityConfiguration内部定义的Bean中,最为重要的两个:

  3. setFilterChainProxySecurityConfigurer方法则是为了收集配置类并创建webSecurity

  4. springSecurityFilterChain该方法的目的是为了使用webSecurity构建FilterChainProxy

    3.1.1、setFilterChainProxySecurityConfigurer

    1. @Autowired(required = false)
    2. public void setFilterChainProxySecurityConfigurer(
    3. ObjectPostProcessor<Object> objectPostProcessor,
    4. @Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
    5. throws Exception {
    6. webSecurity = objectPostProcessor
    7. .postProcess(new WebSecurity(objectPostProcessor));
    8. if (debugEnabled != null) {
    9. webSecurity.debug(debugEnabled);
    10. }
    11. webSecurityConfigurers.sort(AnnotationAwareOrderComparator.INSTANCE);
    12. Integer previousOrder = null;
    13. Object previousConfig = null;
    14. for (SecurityConfigurer<Filter, WebSecurity> config : webSecurityConfigurers) {
    15. Integer order = AnnotationAwareOrderComparator.lookupOrder(config);
    16. if (previousOrder != null && previousOrder.equals(order)) {
    17. throw new IllegalStateException(
    18. "@Order on WebSecurityConfigurers must be unique. Order of "
    19. + order + " was already used on " + previousConfig + ", so it cannot be used on "
    20. + config + " too.");
    21. }
    22. previousOrder = order;
    23. previousConfig = config;
    24. }
    25. for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
    26. webSecurity.apply(webSecurityConfigurer);
    27. }
    28. this.webSecurityConfigurers = webSecurityConfigurers;
    29. }
    首先这个方法有两个参数,两个参数都会进行注入。
    第一个参数objectPostProcessor是一个后置处理器,默认的实现是AutowireBeanFactoryObjectPostProcessor,主要是为了将new出来的对象注入到Spring容器中。
    第二个参数方法webSecurityConfigurers是一个集合,这个集合里存放的都是SecurityConfigurer,前面分析过的过滤器链中过滤器的配置器,包括WebSecurityConfigurerAdapter的子类,都是SecurityConfigurer的实现类。根据@value注解的描述,我们可以知道,这个集合中的数据来自autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()方法。
    WebSecurityConfiguration中定义了该实例:
    1. @Bean
    2. public static AutowiredWebSecurityConfigurersIgnoreParents autowiredWebSecurityConfigurersIgnoreParents(
    3. ConfigurableListableBeanFactory beanFactory) {
    4. return new AutowiredWebSecurityConfigurersIgnoreParents(beanFactory);
    5. }
    它的getWebSecurityConfigurers方法来看下:
    1. public List<SecurityConfigurer<Filter, WebSecurity>> getWebSecurityConfigurers() {
    2. List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers = new ArrayList<>();
    3. Map<String, WebSecurityConfigurer> beansOfType = beanFactory
    4. .getBeansOfType(WebSecurityConfigurer.class);
    5. for (Entry<String, WebSecurityConfigurer> entry : beansOfType.entrySet()) {
    6. webSecurityConfigurers.add(entry.getValue());
    7. }
    8. return webSecurityConfigurers;
    9. }
    可以看到,其实就是从beanFactory工厂中查询到WebSecurityConfigurer的实例返回。
    WebSecurityConfigurer的实例其实就是WebSecurityConfigurerAdapter,如果没有自定义WebSecurityConfigurerAdapter,那么默认使用的就是SpringBootWebSecurityConfiguration中自定义的WebSecurityConfigurerAdapter
    当然我们可能也自定义了WebSecurityConfigurerAdapter,而且如果我们配置了多个过滤器链(多个HttpSecurity配置),那么WebSecurityConfigurerAdapter的实例也将有多个。所以这里返回的是List集合。
    至此,我们搞明白了setFilterChainProxySecurityConfigurer方法的两个参数,回到该方法继续分析。
    接下来创建了webSecurity对象,并且放到objectPostProcessor中处理了一下,也就是new出来的对象存入到Spring容器中。
    调用webSecurityConfigurers.sort方法对WebSecurityConfigurerAdapter进行排序,如果我们配置了多个WebSecurityConfigurerAdapter实例(多个过滤器链),那么我们要通过@Order注解对其进行排序,以便分出一个优先级,而且这个优先级还不能相同。所以接下来的for循环中就是判断这个优先级是否相同的,要是有,直接抛出异常。
    最后,遍历webSecurityConfigurers,并将其数据挨个配置到webSecurity中。webSecurity.apply方法会将这些配置存入AbstractConfiguredSecurityBuilder.configurers属性中。
    这就是setFilterChainProxySecurityConfigurer方法的逻辑,可以看到,它主要是在构造WebSecurity对象。

    3.1.2、springSecurityFilterChain

    WebSecurityConfiguration中第二个比较关键的方法是springSecurityFilterChain,该方法是在上个方法执行之后执行,该方法的目的是构建过滤器链。
    1. @Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
    2. public Filter springSecurityFilterChain() throws Exception {
    3. boolean hasConfigurers = webSecurityConfigurers != null
    4. && !webSecurityConfigurers.isEmpty();
    5. if (!hasConfigurers) {
    6. WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor
    7. .postProcess(new WebSecurityConfigurerAdapter() {
    8. });
    9. webSecurity.apply(adapter);
    10. }
    11. return webSecurity.build();
    12. }
    这里首先会判断有没有webSecurityConfigurers,一般来说都是有的,即使你没有配置,还有一个默认的。当然,如果不存在的话,这里会现场new一个出来,然后调用apply方法。
    🎨最最关键的就是最后的webSecurity.build方法了,这个方法的调用就是去构建过滤器链了。
    根据深入理解Spring Security配置与构建(源码篇)一文中的介绍,这个build方法最终是在AbstractConfiguredSecurityBuilder#dobuild方法中执行的。
    1. @Override
    2. protected final O doBuild() throws Exception {
    3. synchronized (configurers) {
    4. buildState = BuildState.INITIALIZING;
    5. beforeInit();
    6. init();
    7. buildState = BuildState.CONFIGURING;
    8. beforeConfigure();
    9. configure();
    10. buildState = BuildState.BUILDING;
    11. O result = performBuild();
    12. buildState = BuildState.BUILT;
    13. return result;
    14. }
    15. }
    这里会记录下来整个项目的构建状态。比较重要的方法,initconfigureperformBuild方法。
    init方法会遍历所有的WebSecurityConfigurerAdapter,并执行其init方法。WebSecurityConfigurerAdapter#init方法主要是做HttpSecurity的初始化工作,init方法在执行时,会涉及到HttpSecurity的初始化,而HttpSecurity的初始化需要配置AuthenticationManager,所以这里最终还会涉及到一些全局的AuthenticationManagerBuilder及相关属性的初始化,具体可参考深入理解AuthenticationManagerBuilder(源码篇),需要注意的是,AuthenticationManager初始化的时候也会来到这个doBuild方法中。
    configure方法会遍历所有的WebSecurityConfigurerAdapter,并执行其configure方法。WebSecurityConfigurerAdapter#configure方法默认是一个空方法,开发者可以自己重写该方法定义自己的WebSecurity
    最后调用performBuild方法进行构建,这个最终执行的是WebSecurity#performBuild方法,
    WebSecurity#performBuild方法执行的过程,也是过滤器链构建的过程。里面会调用到过滤器链的构建方法,也就是默认的十多个过滤器会挨个构建,这个构建过程也会调用到这个doBuild方法。

    3.2、@EnableGlobalAuthentication

    @EnableWebSecurity注解除了过滤器链的构建,还有一个而注解就是@EnableGlobalAuthentication
    1. @Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
    2. @Target(value = { java.lang.annotation.ElementType.TYPE })
    3. @Documented
    4. @Import(AuthenticationConfiguration.class)
    5. @Configuration
    6. public @interface EnableGlobalAuthentication {
    7. }
    可以看到,该注解的主要作用就是导入AuthenticationConfiguration配置,该配置在深入理解AuthenticationManagerBuilder(源码篇)中已经介绍过了。