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...。
通过阅读文档我们知道:
- Spring Security通过filter实现。
- filter生效过程如下图,其中Spring Security会配置n多个filter来实现目标:
- 而Spring Security的多个过滤器并不是并行插入到Servlet的filte chain中的,而是以一个filter的方式插入进去,这个filter就是DelegatingFilterProxy:
- DelegatingFilterProxy包含一个由SecurityFilterChain组成的FilterChainProxy:
- 而正是这个SecurityFilterChain包含一系列用来实现Spring Security的过滤器filters:
通过以上分析,我们知道Spring Security就是通过组装在FilterChainProxy中的SecurityFilterChain实现的,SecurityFilterChain中包含了一系列过滤器,正是这些过滤器实现了Spring Security的所有功能。
所以,第一步,我们先从源码的角度分析Spring Security的SecurityFilterChain的初始化过程。
Spring Security的配置类在SpringBoot框架下不需要通过类似@EnableWebSecurity的方式启用,SpringBoot的自动装配机制会自动启用Spring Security的配置。
HttpSecutiry类结构
Spring Security的Filter最终是通过SecurityBuilder创建的,SecurityBuilder持有各过滤器的配置器,最后通过配置器生成过滤器并组装到SecurityFilterChain中。
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中生效。
下回分解!!!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。