Spring Security的初始化过程(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-security/reference/servlet/architecture.html

通过阅读文档我们知道:

1. Spring Security通过filter实现。

2. filter生效过程如下图,其中Spring Security会配置n多个filter来实现目标:

![image.png](/img/bVc4owN)

3. 而Spring Security的多个过滤器并不是并行插入到Servlet的filte chain中的,而是以一个filter的方式插入进去,这个filter就是DelegatingFilterProxy:

![image.png](/img/bVc4owP)

4. DelegatingFilterProxy包含一个由SecurityFilterChain组成的FilterChainProxy:

![image.png](/img/bVc4owX)

4. 而正是这个SecurityFilterChain包含一系列用来实现Spring Security的过滤器filters:

![image.png](/img/bVc4ow2)

***通过以上分析,我们知道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](/img/bVc4ozN)

#### 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环绕切入底层原理](https://segmentfault.com/a/1190000042806063)

下一篇 [Spring Security的初始化过程(2)](https://segmentfault.com/a/1190000042973182)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值