1

Spring Security可以通过xml配置或者注解方式初始化,其实只是配置方式不同,初始化过程大致相同。

所以我们只分析通过注解方式的初始化。注解方式的初始化也会根据你的项目采用的框架不同而有所不同,我们只分析SpringBoot框架下的Spring Security的初始化过程。

我们简单先罗列一下与Spring Security有关的配置在Spring.facories文件中的自动装配类,只是有个直观印象就可以,不会逐一做分析:

org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.security.rsocket.RSocketSecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.saml2.Saml2RelyingPartyAutoConfiguration,\
org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\
org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.client.reactive.ReactiveOAuth2ClientAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.resource.reactive.ReactiveOAuth2ResourceServerAutoConfiguration,\

Spring Security的初始化过程确实比较复杂,配置类非常多,刚开始确实有点无从下手的感觉。

所以,决定先从官网上的文档入手。

参考:https://docs.spring.io/spring...

通过阅读文档我们知道:

  1. Spring Security通过filter实现。
  2. filter生效过程如下图,其中Spring Security会配置n多个filter来实现目标:
    image.png
  3. 而Spring Security的多个过滤器并不是并行插入到Servlet的filte chain中的,而是以一个filter的方式插入进去,这个filter就是DelegatingFilterProxy:
    image.png
  4. DelegatingFilterProxy包含一个由SecurityFilterChain组成的FilterChainProxy:
    image.png
  5. 而正是这个SecurityFilterChain包含一系列用来实现Spring Security的过滤器filters:
    image.png

通过以上分析,我们知道Spring Security就是通过组装在FilterChainProxy中的SecurityFilterChain实现的,SecurityFilterChain中包含了一系列过滤器,正是这些过滤器实现了Spring Security的所有功能。

所以,第一步,我们先从源码的角度分析Spring Security的SecurityFilterChain的初始化过程。

Spring Security的配置类在SpringBoot框架下不需要通过类似@EnableWebSecurity的方式启用,SpringBoot的自动装配机制会自动启用Spring Security的配置。

HttpSecutiry类结构

Spring Security的Filter最终是通过SecurityBuilder创建的,SecurityBuilder持有各过滤器的配置器,最后通过配置器生成过滤器并组装到SecurityFilterChain中。
image.png

HttpSecurityConfiguration

HttpSecurityConfiguration是SpringSecurity的一个重要配置类,他的一个重要作用是初始化HttpSecurity对象。

通过httpSecurity()方法创建:

@Bean(HTTPSECURITY_BEAN_NAME)
    @Scope("prototype")
    HttpSecurity httpSecurity() throws Exception {
        WebSecurityConfigurerAdapter.LazyPasswordEncoder passwordEncoder = new WebSecurityConfigurerAdapter.LazyPasswordEncoder(
                this.context);
        AuthenticationManagerBuilder authenticationBuilder = new WebSecurityConfigurerAdapter.DefaultPasswordEncoderAuthenticationManagerBuilder(
                this.objectPostProcessor, passwordEncoder);
        authenticationBuilder.parentAuthenticationManager(authenticationManager());
        HttpSecurity http = new HttpSecurity(this.objectPostProcessor, authenticationBuilder, createSharedObjects());
        // @formatter:off
        http
            .csrf(withDefaults())
            .addFilter(new WebAsyncManagerIntegrationFilter())
            .exceptionHandling(withDefaults())
            .headers(withDefaults())
            .sessionManagement(withDefaults())
            .securityContext(withDefaults())
            .requestCache(withDefaults())
            .anonymous(withDefaults())
            .servletApi(withDefaults())
            .apply(new DefaultLoginPageConfigurer<>());
        http.logout(withDefaults());
        // @formatter:on
        return http;
    }

创建HttpSecurity对象并调用一系列方法装配configurers属性,configurers是一个包含各安全过滤器配置器的集合。

Spring Security的过滤器是通过对应的配置器生成的,所以我们的目光要先聚焦在以上生成配置器的代码上。

比如http.csrf(withDefaults())调用会生成Spring Security的防跨域请求过滤器的配置器,最后通过配置器生成过滤器(后面我们会分析源码)。

Spring Security的其他过滤器也是通过以上方式、首先生成过滤器的配置器、然后再有配置器生成过滤器,再将过滤器组装在SecurityFilterChain中从而生效的。具体的生成配置器的代码就不一一贴出了。

HttpSecurityConfiguration的其他初始化过程暂时忽略。

SpringBootWebSecurityConfiguration

SpringBootWebSecurityConfiguration只有一个方法defaultSecurityFilterChain,用来创建SecurityFilterChain:

@Configuration(proxyBeanMethods = false)
@ConditionalOnDefaultWebSecurity
@ConditionalOnWebApplication(type = Type.SERVLET)
class SpringBootWebSecurityConfiguration {

    @Bean
    @Order(SecurityProperties.BASIC_AUTH_ORDER)
    SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
        http.authorizeRequests().anyRequest().authenticated().and().formLogin().and().httpBasic();
        return http.build();
    }

}

defaultSecurityFilterChain方法有一个参数:HttpSecurity,该对象的初始化过程我们前面已经分析过了,我们直接看http.build()。

AbstractSecurityBuilder#build()

build方法在父类AbstractSecurityBuilde中实现:

@Override
    public final O build() throws Exception {
        if (this.building.compareAndSet(false, true)) {
            this.object = doBuild();
            return this.object;
        }
        throw new AlreadyBuiltException("This object has already been built");
    }

AbstractConfiguredSecurityBuilder#doBuild

doBuild方法的一系列调用方法:

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

我们重点关注两个,一个是configure(),一个是performBuild()。

AbstractConfiguredSecurityBuilder#configure()

configure方法获取到configures,逐个调用其configure方法。configures我们前面分析HttpSecurityConfiguration源码的时候已经分析过了,是在HttpSecurity被创建之后装配出来的。

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

被装配进来的众多配置器我们暂时就不一一分析了,还是以跨域过滤配置器为例,了解一下过滤器的创建和配置过程,其他配置器的配置过程大同小异:

@Override
    public void configure(H http) {
        CsrfFilter filter = new CsrfFilter(this.csrfTokenRepository);
        ...省略n行代码
        filter = postProcess(filter);
        http.addFilter(filter);
    }

可以看到configure方法创建过滤器,并将过滤器加入到HttpSecurity的filters容器中。
简单看一眼addFilter方法:

@Override
    public HttpSecurity addFilter(Filter filter) {
        Integer order = this.filterOrders.getOrder(filter.getClass());
        if (order == null) {
            throw new IllegalArgumentException("The Filter class " + filter.getClass().getName()
                    + " does not have a registered order and cannot be added without a specified order. Consider using addFilterBefore or addFilterAfter instead.");
        }
        this.filters.add(new OrderedFilter(filter, order));
        return this;
    }

我们只要了解到所有的filter再加入到filters中的时候都是有顺序的,这个顺序是在HttpSecurity初始化的过程中指定的。

好了,到现在为止,我们知道各过滤器已经被加入到创建器(builder)的容器filters中了。

接下来,我们可以猜测得到,肯定就是创建器要通过什么方式将他的filters中的各过滤器装配的SecurityFilterChans中了。

继续分析......

HttpSecurity#performBuilder()

performBuilder()方法又绕回到HttpSecurity中实现,将创建好的过滤器fiters作为参数,创建DefaultSecurityFilterChain:

    @Override
    protected DefaultSecurityFilterChain performBuild() {
        this.filters.sort(OrderComparator.INSTANCE);
        List<Filter> sortedFilters = new ArrayList<>(this.filters.size());
        for (Filter filter : this.filters) {
            sortedFilters.add(((OrderedFilter) filter).filter);
        }
        return new DefaultSecurityFilterChain(this.requestMatcher, sortedFilters);
    }

DefaultSecurityFilterChain才是我们的主角!创建好的DefaultSecurityFilterChain注入到Spring的Ioc容器中,之后组装到FilterChainProxy中生效。

下回分解!!!

上一篇 Spring事务控制AOP环绕切入底层原理
下一篇 Spring Security的初始化过程(2)


45 声望17 粉丝