? ? ? Spring Security是一個(gè)強(qiáng)大的并且高度可定制化的訪問控制框架。?它基于spring應(yīng)用。
Spring Security是聚焦于為java應(yīng)用提供授權(quán)和驗(yàn)證的框架。像所有的spring項(xiàng)目一樣,Spring Security真正的強(qiáng)大在于可以非常簡(jiǎn)單的拓展功能來實(shí)現(xiàn)自定義的需求。
? ? ? 在分析SpringBoot集成的SpringSecurity源碼時(shí),一般可以分為兩部分來分析Spring安全框架的源碼。
? ? ? 一、SpringSecurity在SpringBoot框架的啟動(dòng)過程中的加載過程。
? ? ? 二、SpringSecurity在請(qǐng)求執(zhí)行過程當(dāng)中的執(zhí)行過程。
? ? ?現(xiàn)在我根據(jù)上面的兩個(gè)過程對(duì)SpringSecurity的源碼進(jìn)行分析。
? ? ?在分析時(shí)我們需要在springboot項(xiàng)目中引入SpringSecurity的maven依賴配置。該配置如下所示:
<!--spring 安全框架-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
注意:我分析的springsecurity版本是5.0.7-RELEASE?
?SpringSecurity在SpringBoot框架的啟動(dòng)過程中的加載過程。
? ? ?SpringSecurity框架在調(diào)用過程中通過配置的方式往spring容器中注入很多bean,為調(diào)用過程中做準(zhǔn)備。
? ? ?在SpringSecurity中存在很多配置類,負(fù)責(zé)在springboot啟動(dòng)時(shí)往容器注入bean。本文主要分析一下設(shè)計(jì)注入的類。
? ? ?在SpringSecurity框架中有三個(gè)非常核心的類和接口,分別是
? ? ? ? ? ? 1.SecurityFilterChain接口
? ? ? ? ? ? 2.FilterChainProxy類
? ? ? ? ? ? 3.DelegatingFilterProxy類
這個(gè)三個(gè)接口和類的相互之間的步驟關(guān)系如下:
? ? 第一步:生成一個(gè)FilterChainProxy類型的對(duì)象,其中的屬性filterChains是SecurityFilterChain類型的List集合,該對(duì)象是一個(gè)被spring容器管理名稱為springSecurityFilterChain類型為FilterChainProxy的bean。
? ? 第二步:生成一個(gè)DelegatingFilterProxy類型的對(duì)象,將beanName即springSecurityFilterChain作為DelegatingFilterProxy類型對(duì)象屬性targetBeanName的值,供后面請(qǐng)求時(shí)獲取bean。這樣FilterChainProxy類型的對(duì)象就被DelegatingFilterProxy類型的對(duì)象委托管理了。
? ? ??DelegatingFilterProxy對(duì)象的生成是tomcat啟動(dòng)過程中會(huì)調(diào)用所有繼承了RegistrationBean類的
onStartUp方法,最終調(diào)用了實(shí)現(xiàn)類中的addRegistration方法。RegistrationBean類中onStartUp方法的調(diào)用邏輯可以參考我寫的springMvc分析第一章
SpringMvc源碼分析(一):?jiǎn)?dòng)tomcat服務(wù)器,加載DispatcherServlet并將DispatcherServlet納入tomcat管理_xl649138628的博客-CSDN博客
其調(diào)用鏈路是
? ? +RegistrationBean#onStartup
? ? ? ? +DynamicRegistrationBean#register
? ? ? ? ? ?+AbstractFilterRegistrationBean#addRegistration(往StandardContext設(shè)置攔截器類型為DelegatingFilterProxy)
? ? ? ? ? ? ? +DelegatingFilterProxyRegistrationBean#getFilter()
第三步:前端發(fā)起請(qǐng)求時(shí),調(diào)用了DelegatingFilterProxy類型的攔截器執(zhí)行doFilter方法。doFilter方法獲取被委托的對(duì)象FilterChainProxy并調(diào)用其doFilter方法。FilterChainProxy的doFilter方法,執(zhí)行獲取到的所有的攔截器然后再獲取代理對(duì)象執(zhí)行容器加載時(shí)保存的攔截器再執(zhí)行。
本文主要分析第一步和第二步。
1.生成一個(gè)FilterChainProxy類型的對(duì)象
? ?1.1分析配置文件并生成beanName為springSecurityFilterChain的Bean
? ? 1.1.1 SpringSecurity攔截器示意圖
? ? 在SpringSecurity官方文檔中可以看到如下示意圖。
? ? 該圖中清楚的可以看到請(qǐng)求訪問時(shí),DelegatingFilterProxy管理FilterChainProxy,F(xiàn)ilterChainProxy里調(diào)用SecurityFilterChain類型的過濾器。
下面我會(huì)詳細(xì)的分析這三個(gè)類和對(duì)象是怎么關(guān)聯(lián)起來?。
?1.1.2 安全框架的配置類及如何加載
? ? 在springboot啟動(dòng)過程中會(huì)獲取spring.factories配置文件里的配置類并加載到spring容器中,觀察spring.factories配置文件里的配置內(nèi)容,涉及到springsecurity的如下圖紅框處所示。
? ? ? 我們首先關(guān)注SecurityAutoConfiguration和SecurityFilterAutoConfiguration這兩個(gè)配置類。
深入分析這些類,我們可以看到配置類的關(guān)系如下圖所示:
1.1.3配置類是如何獲取到?springSecurityFilterChain這個(gè)bean的
?首先分析SecurityAutoConfiguration配置類
@Configuration
@ConditionalOnClass(DefaultAuthenticationEventPublisher.class)
//導(dǎo)入屬性配置文件,內(nèi)部聲明了user類
@EnableConfigurationProperties(SecurityProperties.class)
//導(dǎo)入并加載下面三個(gè)配置類到Spring容器管理
@Import({ SpringBootWebSecurityConfiguration.class, WebSecurityEnablerConfiguration.class,
SecurityDataConfiguration.class })
public class SecurityAutoConfiguration {
//發(fā)布認(rèn)證事件,當(dāng)AuthenticationEventPublisher bean不存在時(shí)加載
@Bean
@ConditionalOnMissingBean(AuthenticationEventPublisher.class)
public DefaultAuthenticationEventPublisher authenticationEventPublisher(
ApplicationEventPublisher publisher) {
//內(nèi)部實(shí)現(xiàn)就是spring的ApplicationEventPublisher,
// 用于springsecurity各種權(quán)限時(shí)間的交互,如登陸失敗,會(huì)發(fā)布一個(gè)事件,
// 然后通知其它組件做出相應(yīng)的響應(yīng)
return new DefaultAuthenticationEventPublisher(publisher);
}
}
分析該配置類里導(dǎo)入的三個(gè)配置類SpringBootWebSecurityConfiguration.class, WebSecurityEnablerConfiguration.class,SecurityDataConfiguration.class
1.SpringBootWebSecurityConfiguration配置類
作用是WebSecurityConfigurerAdapter 類存在但是bean對(duì)象不存在時(shí)注冊(cè)默認(rèn)的WebSecurityConfigurerAdapter 類型是DefaultConfigurerAdapter的bean。
@ConditionalOnClass(WebSecurityConfigurerAdapter.class)
@ConditionalOnMissingBean(WebSecurityConfigurerAdapter.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
public class SpringBootWebSecurityConfiguration {
//往容器中注入WebSecurityConfigurerAdapter類型的Bean,后面的邏輯會(huì)使用
@Configuration
@Order(SecurityProperties.BASIC_AUTH_ORDER)
static class DefaultConfigurerAdapter extends WebSecurityConfigurerAdapter {
}
}
2.WebSecurityEnablerConfiguration配置類
該類文件如下,可以看到其中聲明了@EnableWebSecurity注解
//WebSecurityConfigurerAdapter bean對(duì)象存在
@ConditionalOnBean(WebSecurityConfigurerAdapter.class)
//沒有名稱為springSecurityFilterChain的bean
@ConditionalOnMissingBean(name = BeanIds.SPRING_SECURITY_FILTER_CHAIN)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@EnableWebSecurity
public class WebSecurityEnablerConfiguration {
}
進(jìn)入@EnableWebSecurity注解
@Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
@Target(value = { java.lang.annotation.ElementType.TYPE })
@Documented
@Import({ WebSecurityConfiguration.class,
SpringWebMvcImportSelector.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;
}
可以看到引入了WebSecurityConfiguration和SpringWebMvcImportSelector兩個(gè)配置類,聲明了@EnableGlobalAuthentication注解。先研究WebSecurityConfiguration類
2.1WebSecurityConfiguration類
? ? 類中的英文注釋如下:
* Uses a {@link WebSecurity} to create the {@link FilterChainProxy} that performs the web
* based security for Spring Security. It then exports the necessary beans. Customizations
* can be made to {@link WebSecurity} by extending {@link WebSecurityConfigurerAdapter}
* and exposing it as a {@link Configuration} or implementing
* {@link WebSecurityConfigurer} and exposing it as a {@link Configuration}. This
* configuration is imported when using {@link EnableWebSecurity}.
意思是:基于SpringSecurity框架的安全性執(zhí)行web使用WebSecurity去創(chuàng)建一個(gè)FilterChainProxy。然后輸出需要的bean.通過繼承WebSecurityConfigurerAdapter并且聲明為一個(gè)Configuration配置或者繼承WebSecurityConfigurer并且聲明為一個(gè)Configuration配置來實(shí)現(xiàn)定制化。這個(gè)WebSecurityConfiguration類是通過EnableWebSecurity注解引入的。
分析WebSecurityConfiguration類重點(diǎn)關(guān)注以下代碼
@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public Filter springSecurityFilterChain() throws Exception {
boolean hasConfigurers = webSecurityConfigurers != null
&& !webSecurityConfigurers.isEmpty();
if (!hasConfigurers) { //注意hasConfigurers為true,此部分邏輯不執(zhí)行
WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor
.postProcess(new WebSecurityConfigurerAdapter() {
});
//往AbstractConfiguredSecurityBuilder類型對(duì)象的configurers屬性
//(該屬性是LinkedHashMap類型)中添加了一個(gè)WebSecurityConfigurerAdapter類型的數(shù)據(jù)
webSecurity.apply(adapter);
}
return webSecurity.build();
}
該端代碼往spring容器中注入了一個(gè)名稱為springSecurityFilterChain的bean。
1.1.4分析springSecurityFilterChain,并分析springSecurityFilterChain是如何獲取到過濾器并管理過濾器的
再源碼中可以看到這段代碼
WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor .postProcess(new WebSecurityConfigurerAdapter() { });
該代碼是通過?objectObjectPostProcessor調(diào)用的,objectObjectPostProcessor對(duì)象是通過
@Autowired(required = false)
private ObjectPostProcessor<Object> objectObjectPostProcessor;
注入到WebSecurityConfiguration配置類對(duì)象里的。objectObjectPostProcessor這個(gè)bean是通過@EnableWebSecurity注解里的@EnableGlobalAuthentication里的引入的AuthenticationConfiguration.class里引入的ObjectPostProcessorConfiguration.class注入的。注入代碼如下:
@Configuration
public class ObjectPostProcessorConfiguration {
@Bean
public ObjectPostProcessor<Object> objectPostProcessor(
AutowireCapableBeanFactory beanFactory) {
return new AutowireBeanFactoryObjectPostProcessor(beanFactory);
}
}
繼續(xù)回到objectObjectPostProcessor .postProcess(new WebSecurityConfigurerAdapter() { });方法。因?yàn)閛bjectObjectPostProcessor是AutowireBeanFactoryObjectPostProcessor類型的,所以調(diào)用的是AutowireBeanFactoryObjectPostProcessor的postProcess方法。
在postProcess方法的源碼中,可以看到其將傳入的對(duì)象生成一個(gè)bean并注入到spring容器中。
由于這個(gè)對(duì)象是new WebSecurityConfigurerAdapter()生成的,所以生成的是一個(gè)WebSecurityConfigurerAdapter類型的bean。
public <T> T postProcess(T object) {
if (object == null) {
return null;
}
T result = null;
try {
result = (T) this.autowireBeanFactory.initializeBean(object,
object.toString());
}
catch (RuntimeException e) {
Class<?> type = object.getClass();
throw new RuntimeException(
"Could not postProcess " + object + " of type " + type, e);
}
this.autowireBeanFactory.autowireBean(object);
if (result instanceof DisposableBean) {
this.disposableBeans.add((DisposableBean) result);
}
if (result instanceof SmartInitializingSingleton) {
this.smartSingletons.add((SmartInitializingSingleton) result);
}
return result;
}
繼續(xù)分析springSecurityFilterChain方法里的webSecurity.apply(adapter)方法。我們發(fā)現(xiàn)webSecurity對(duì)象的賦值是在以下源碼中實(shí)現(xiàn)的
@Autowired(required = false)
public void setFilterChainProxySecurityConfigurer(
ObjectPostProcessor<Object> objectPostProcessor,
@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
throws Exception {
webSecurity = objectPostProcessor
.postProcess(new WebSecurity(objectPostProcessor));
if (debugEnabled != null) {
webSecurity.debug(debugEnabled);
}
//根據(jù)繼承WebSecurityConfigurer配置類的@Order注解進(jìn)行排序
Collections.sort(webSecurityConfigurers, AnnotationAwareOrderComparator.INSTANCE);
Integer previousOrder = null;
Object previousConfig = null;
for (SecurityConfigurer<Filter, WebSecurity> config : webSecurityConfigurers) {
Integer order = AnnotationAwareOrderComparator.lookupOrder(config);
//如果有同等級(jí)的配置類拋出異常
if (previousOrder != null && previousOrder.equals(order)) {
throw new IllegalStateException(
"@Order on WebSecurityConfigurers must be unique. Order of "
+ order + " was already used on " + previousConfig + ", so it cannot be used on "
+ config + " too.");
}
previousOrder = order;
previousConfig = config;
}
for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
webSecurity.apply(webSecurityConfigurer);
}
//將自定義繼承了WebSecurityConfigurer的配置類集合賦值給webSecurityConfigurers屬性
this.webSecurityConfigurers = webSecurityConfigurers;
}
在上面的代碼中我們發(fā)現(xiàn)入?yún)?/p>
@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers
這段代碼的意思是調(diào)用autowiredWebSecurityConfigurersIgnoreParents對(duì)象里的getWebSecurityConfigurers方法。autowiredWebSecurityConfigurersIgnoreParents對(duì)象是通過WebSecurityConfiguration類里的以下方法注入的。
@Bean
public static AutowiredWebSecurityConfigurersIgnoreParents autowiredWebSecurityConfigurersIgnoreParents(
ConfigurableListableBeanFactory beanFactory) {
return new AutowiredWebSecurityConfigurersIgnoreParents(beanFactory);
}
觀察AutowiredWebSecurityConfigurersIgnoreParents類里的getWebSecurityConfigurers方法。
該方法的作用是獲取WebSecurityConfigurer類型的bean。并返回一個(gè)list集合。該bean對(duì)象是開發(fā)者自定義的各種各樣繼承自WebSecurityConfigurerAdapter的配置類。如果開發(fā)者沒有自定義任何配置類,那么這里獲取到的就是前面所講的SpringBootWebSecurityConfiguration 類中提供的默認(rèn)配置類,將獲取到的所有配置類實(shí)例放入webSecurityConfigurers集合中并返回。
public List<SecurityConfigurer<Filter, WebSecurity>> getWebSecurityConfigurers() {
List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers = new ArrayList<SecurityConfigurer<Filter, WebSecurity>>();
Map<String, WebSecurityConfigurer> beansOfType = beanFactory
.getBeansOfType(WebSecurityConfigurer.class);
//循環(huán)將所有的過濾器放到webSecurityConfigurers屬性中。
for (Entry<String, WebSecurityConfigurer> entry : beansOfType.entrySet()) {
webSecurityConfigurers.add(entry.getValue());
}
return webSecurityConfigurers;
}
另外一個(gè)入?yún)bjectPostProcessor<Object> objectPostProcessor 該對(duì)象是Spring注入的AutowireBeanFactoryObjectPostProcessor的bean.該bean的生成在上文有分析過。
webSecurity = objectPostProcessor .postProcess(new WebSecurity(objectPostProcessor));
該段代碼生成了一個(gè)WebSecurity 類型的bean并對(duì)WebSecurityConfiguration類的webSecurity屬性賦值。
for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
webSecurity.apply(webSecurityConfigurer);
}
在上面的代碼中循環(huán)獲取到的自定義配置類??匆娙缦麓a
public <C extends SecurityConfigurer<O, B>> C apply(C configurer) throws Exception {
add(configurer);
return configurer;
}
重點(diǎn)關(guān)注上面add方法,源碼如下:
private <C extends SecurityConfigurer<O, B>> void add(C configurer) throws Exception {
Assert.notNull(configurer, "configurer cannot be null");
Class<? extends SecurityConfigurer<O, B>> clazz = (Class<? extends SecurityConfigurer<O, B>>) configurer
.getClass();
synchronized (configurers) {
if (buildState.isConfigured()) {
throw new IllegalStateException("Cannot apply " + configurer
+ " to already built object");
}
List<SecurityConfigurer<O, B>> configs = allowConfigurersOfSameType ? this.configurers
.get(clazz) : null;
if (configs == null) {
configs = new ArrayList<SecurityConfigurer<O, B>>(1);
}
configs.add(configurer);
//給configurers屬性里放入以類名為Key,List<SecurityConfigurer<O, B>>為value
//放入List<SecurityConfigurer<O, B>>里的是開發(fā)的自定義攔截器
this.configurers.put(clazz, configs);
if (buildState.isInitializing()) {
this.configurersAddedInInitializing.add(configurer);
}
}
}
這樣所有的開發(fā)自定義的繼承自WebSecurityConfigurer的配置類和SpringBootWebSecurityConfiguration配置類中通過@Bean方式納入Spring容器管理的繼承自WebSecurityConfigurer的WebSecurityConfigurerAdapter類都放到了AbstractConfiguredSecurityBuilder類的configurers屬性中供后面調(diào)用。
即configurers屬性值 = 開發(fā)自定的繼承自WebSecurityConfigurer配置類+SpringBootWebSecurityConfiguration配置類導(dǎo)入的WebSecurityConfigurerAdapter類。
1.2 分析FilterChainProxy類型的代理對(duì)象是如何生成的,并且是如何管理springSecurityFilterChain 這個(gè)bean的。
繼續(xù)分析springSecurityFilterChain方法里的webSecurity.build()方法。在前面我們知道了webSecurity對(duì)象是WebSecurity類型的。
public final O build() throws Exception {
if (this.building.compareAndSet(false, true)) {
//關(guān)注此方法
this.object = doBuild();
return this.object;
}
throw new AlreadyBuiltException("This object has already been built");
}
@Override
protected final O doBuild() throws Exception {
synchronized (configurers) {
buildState = BuildState.INITIALIZING;
//待實(shí)現(xiàn)方法
beforeInit();
//調(diào)用的是AbstractConfiguredSecurityBuilder類里的init方法
init();
buildState = BuildState.CONFIGURING;
//待實(shí)現(xiàn)方法
beforeConfigure();
//調(diào)用的是AbstractConfiguredSecurityBuilder類里的configure方法
configure();
buildState = BuildState.BUILDING;
//調(diào)用的是WebSecurity類里的performBuild方法。
O result = performBuild();
buildState = BuildState.BUILT;
return result;
}
}
?其中init方法調(diào)用了開發(fā)者自定義的實(shí)現(xiàn)了WebSecurityConfigurer
的配置類里的init方法及WebSecurityConfigurerAdapter類里的init方法。
以下是
private void init() throws Exception {
Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
for (SecurityConfigurer<O, B> configurer : configurers) {
configurer.init((B) this);
}
for (SecurityConfigurer<O, B> configurer : configurersAddedInInitializing) {
configurer.init((B) this);
}
}
public void init(final WebSecurity web) throws Exception {
//生成一個(gè)HttpSecurity類型的對(duì)象
final HttpSecurity http = getHttp();
//addSecurityFilterChainBuilder對(duì)securityFilterChainBuilders屬性賦值
web.addSecurityFilterChainBuilder(http).postBuildAction(new Runnable() {
public void run() {
FilterSecurityInterceptor securityInterceptor = http
.getSharedObject(FilterSecurityInterceptor.class);
web.securityInterceptor(securityInterceptor);
}
});
}
?getHttp方法用于生成一個(gè)HttpSecurity類型的對(duì)象。
protected final HttpSecurity getHttp() throws Exception {
if (http != null) {
return http;
}
DefaultAuthenticationEventPublisher eventPublisher = objectPostProcessor
.postProcess(new DefaultAuthenticationEventPublisher());
localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);
AuthenticationManager authenticationManager = authenticationManager();
authenticationBuilder.parentAuthenticationManager(authenticationManager);
Map<Class<? extends Object>, Object> sharedObjects = createSharedObjects();
http = new HttpSecurity(objectPostProcessor, authenticationBuilder,
sharedObjects);
if (!disableDefaults) {
// @formatter:off
http
.csrf().and()
.addFilter(new WebAsyncManagerIntegrationFilter())
.exceptionHandling().and()
.headers().and()
.sessionManagement().and()
.securityContext().and()
.requestCache().and()
.anonymous().and()
.servletApi().and()
.apply(new DefaultLoginPageConfigurer<>()).and()
.logout();
// @formatter:on
ClassLoader classLoader = this.context.getClassLoader();
//SpringFactoriesLoader類的主要作用是通過類路徑下的META-INF/spring.factories文件
//獲取工廠類接口的實(shí)現(xiàn)類,初始化并保存在緩存中,以供Springboot啟動(dòng)過程中各個(gè)階段的調(diào)用。
//SpringFactoriesLoader.loadFactories():是根據(jù)參數(shù)factoryClass獲取spring.factories下配置的所有實(shí)現(xiàn)類實(shí)例,返回List<T>的。
//SpringFactoriesLoader.loadFactoryNames():是根據(jù)參數(shù)factoryClass獲取spring.factories下配置
List<AbstractHttpConfigurer> defaultHttpConfigurers =
SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader);
//將所有AbstractHttpConfigurer類型的對(duì)象放入到AbstractConfiguredSecurityBuilder類的configurers屬性中供后面調(diào)用
for(AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
http.apply(configurer);
}
}
//配置HttpSecurity類型對(duì)象的屬性
configure(http);
return http;
}
?其中configure方法調(diào)用了開發(fā)者自定義的繼承自WebSecurityConfigurerAdapter的配置類里的configure方法
private void configure() throws Exception {
Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
for (SecurityConfigurer<O, B> configurer : configurers) {
configurer.configure((B) this);
}
}
?其中performBuild調(diào)用的是WebSecurity類里的performBuild方法,該方法的作用是FilterChainProxy類型的代理對(duì)象,該代理對(duì)象將攔截器鏈納入代理對(duì)象管理。
@Override
protected Filter performBuild() throws Exception {
Assert.state(
!securityFilterChainBuilders.isEmpty(),
"At least one SecurityBuilder<? extends SecurityFilterChain> needs to be specified. Typically this done by adding a @Configuration that extends WebSecurityConfigurerAdapter. More advanced users can invoke "
+ WebSecurity.class.getSimpleName()
+ ".addSecurityFilterChainBuilder directly");
int chainSize = ignoredRequests.size() + securityFilterChainBuilders.size();
List<SecurityFilterChain> securityFilterChains = new ArrayList<>(
chainSize);
for (RequestMatcher ignoredRequest : ignoredRequests) {
securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
}
//securityFilterChainBuilders取到的是HttpSecurity類型的對(duì)象
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {
securityFilterChains.add(securityFilterChainBuilder.build());
}
//重點(diǎn)關(guān)注此處,將攔截器鏈納入filterChainProxy代理類管理,如果不擴(kuò)展securityFilterChainBuilders屬性,里面只有一個(gè)對(duì)象時(shí)HttpSecurity類型的。
FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
if (httpFirewall != null) {
filterChainProxy.setFirewall(httpFirewall);
}
filterChainProxy.afterPropertiesSet();
Filter result = filterChainProxy;
if (debugEnabled) {
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);
}
//執(zhí)行WebSecurityConfigurerAdapter類init方法里傳入的 run方法,該方法用于
//給filterSecurityInterceptor屬性賦值
postBuildAction.run();
返回的是FilterChainProxy 類型的對(duì)象
return result;
}
這樣?springSecurityFilterChain這個(gè)Bean名實(shí)際上對(duì)應(yīng)的是FilterChainProxy 類型的對(duì)象。
2.?生成一個(gè)DelegatingFilterProxy類型的對(duì)象.
分析spring.factories配置文件里的配置類SecurityFilterAutoConfiguration。
可以看到如下源碼:
@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
//加載SecurityProperties配置類
@EnableConfigurationProperties(SecurityProperties.class)
//AbstractSecurityWebApplicationInitializer和SessionCreationPolicy存在再執(zhí)行配置
@ConditionalOnClass({ AbstractSecurityWebApplicationInitializer.class,
SessionCreationPolicy.class })
//在SecurityAutoConfiguration加載完后再執(zhí)行配置
@AutoConfigureAfter(SecurityAutoConfiguration.class)
public class SecurityFilterAutoConfiguration {
//省略。。。。。
}
在該配置類中通過@Bean的方式往spring容器中注入了一個(gè)bean,該bean的類型是?DelegatingFilterProxyRegistrationBean。
@Bean
@ConditionalOnBean(name = DEFAULT_FILTER_NAME)
public DelegatingFilterProxyRegistrationBean securityFilterChainRegistration(
SecurityProperties securityProperties) {
//初始化對(duì)象,并對(duì)屬性targetBeanName賦值為springSecurityFilterChain
DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean(
DEFAULT_FILTER_NAME);
registration.setOrder(securityProperties.getFilter().getOrder());
registration.setDispatcherTypes(getDispatcherTypes(securityProperties));
return registration;
}
查看DelegatingFilterProxyRegistrationBean的繼承實(shí)現(xiàn)樹,發(fā)現(xiàn)該類實(shí)現(xiàn)了RegistrationBean。RegistrationBean中有一個(gè)onStartup方法,在SpringBoot啟動(dòng)的過程中,tomcat容器會(huì)調(diào)用該方法。
在onStartUp方法中存在register,根據(jù)動(dòng)態(tài)綁定機(jī)制執(zhí)行的是DynamicRegistrationBean里的register方法。
@Override
protected final void register(String description, ServletContext servletContext) {
//重點(diǎn)關(guān)注
D registration = addRegistration(description, servletContext);
if (registration == null) {
logger.info(StringUtils.capitalize(description) + " was not registered "
+ "(possibly already registered?)");
return;
}
//重點(diǎn)關(guān)注
configure(registration);
}
該register方法里調(diào)用了addRegistration方法。該addRegistration方法是一個(gè)抽象方法。調(diào)用的是AbstractFilterRegistrationBean里的addRegistration方法。
protected Dynamic addRegistration(String description, ServletContext servletContext) {
Filter filter = getFilter();
return servletContext.addFilter(getOrDeduceName(filter), filter);
}
?在AbstractFilterRegistrationBean中g(shù)etFilter是調(diào)用的DelegatingFilterProxyRegistrationBean里的getFilter方法。在該方法中this.targetBeanName屬性值是springSecurityFilterChain字符串。
@Override
public DelegatingFilterProxy getFilter() {
return new DelegatingFilterProxy(this.targetBeanName,
getWebApplicationContext()) {
@Override
protected void initFilterBean() throws ServletException {
// Don't initialize filter bean on init()
}
};
}
?分析getFilter方法該方法最終返回了一個(gè)DelegatingFilterProxy類型的對(duì)象。繼續(xù)分析AbstractFilterRegistrationBean里的addRegistration方法。在addRegistration里執(zhí)行完getFilter方法后,程序繼續(xù)執(zhí)行了servletContext.addFilter(getOrDeduceName(filter), filter)這段代碼。在這段代碼中
@Override
protected Dynamic addRegistration(String description, ServletContext servletContext) {
Filter filter = getFilter();
return servletContext.addFilter(getOrDeduceName(filter), filter);
}
? ?一、servletContext是Tomcat里的ApplicationContextFacade類型的對(duì)象。
public ServletContext getServletContext() {
if (context == null) {
context = new ApplicationContext(this);
if (altDDName != null)
context.setAttribute(Globals.ALT_DD_ATTR,altDDName);
}
return (context.getFacade());
}
二、ApplicationContextFacade通過addFilter方法往Tomcat的Servlet里添加了攔截器。這樣攔截器就被Tomcat管理了。
@Override
public FilterRegistration.Dynamic addFilter(String filterName,
Filter filter) {
if (SecurityUtil.isPackageProtectionEnabled()) {
return (FilterRegistration.Dynamic) doPrivileged("addFilter",
new Class[]{String.class, Filter.class},
new Object[]{filterName, filter});
} else {
//設(shè)置的filterName為springSecurityFilterChain,即FilterChainProxy類型
//返回一個(gè)ApplicationFilterRegistration類型的對(duì)象
return context.addFilter(filterName, filter);
}
}
@Override
public FilterRegistration.Dynamic addFilter(String filterName,
Filter filter) {
if (SecurityUtil.isPackageProtectionEnabled()) {
return (FilterRegistration.Dynamic) doPrivileged("addFilter",
new Class[]{String.class, Filter.class},
new Object[]{filterName, filter});
} else {
return context.addFilter(filterName, filter);
}
}
其addFilter方法最終調(diào)用了以下ApplicationContext類里addFilter方法,該方法返回了一個(gè)
ApplicationFilterRegistration類型的對(duì)象。該對(duì)象的構(gòu)造參數(shù)分別是filterDef和context
private FilterRegistration.Dynamic addFilter(String filterName,
String filterClass, Filter filter) throws IllegalStateException {
if (filterName == null || filterName.equals("")) {
throw new IllegalArgumentException(sm.getString(
"applicationContext.invalidFilterName", filterName));
}
if (!context.getState().equals(LifecycleState.STARTING_PREP)) {
//TODO Spec breaking enhancement to ignore this restriction
throw new IllegalStateException(
sm.getString("applicationContext.addFilter.ise",
getContextPath()));
}
FilterDef filterDef = context.findFilterDef(filterName);
// Assume a 'complete' FilterRegistration is one that has a class and
// a name
if (filterDef == null) {
filterDef = new FilterDef();
filterDef.setFilterName(filterName);
context.addFilterDef(filterDef);
} else {
if (filterDef.getFilterName() != null &&
filterDef.getFilterClass() != null) {
return null;
}
}
if (filter == null) {
filterDef.setFilterClass(filterClass);
} else {
filterDef.setFilterClass(filter.getClass().getName());
filterDef.setFilter(filter);
}
//重點(diǎn)關(guān)注此處,filterDef filtername是springSecurityFilterChain,該屬性后面configure方法會(huì)使用
return new ApplicationFilterRegistration(filterDef, context);
}
繼續(xù)回到DynamicRegistrationBean里的register方法。該方法里調(diào)用了configure方法,雖然因?yàn)檎{(diào)用onStartup方法的對(duì)象是DelegatingFilterProxyRegistrationBean,所以雖然DynamicRegistrationBean里有configure方法,實(shí)際上其調(diào)用的是AbstractFilterRegistrationBean里的register方法。該方法最終返回個(gè)ApplicationFilterRegistration類型的對(duì)象。
在configure方法中通過debug發(fā)現(xiàn)其通過registration.addMappingForUrlPatterns(dispatcherTypes, this.matchAfter, DEFAULT_URL_MAPPINGS);文章來源:http://www.zghlxwxcb.cn/news/detail-780355.html
protected void configure(FilterRegistration.Dynamic registration) {
super.configure(registration);
EnumSet<DispatcherType> dispatcherTypes = this.dispatcherTypes;
if (dispatcherTypes == null) {
dispatcherTypes = EnumSet.of(DispatcherType.REQUEST);
}
Set<String> servletNames = new LinkedHashSet<>();
for (ServletRegistrationBean<?> servletRegistrationBean : this.servletRegistrationBeans) {
servletNames.add(servletRegistrationBean.getServletName());
}
servletNames.addAll(this.servletNames);
if (servletNames.isEmpty() && this.urlPatterns.isEmpty()) {
this.logger.info("Mapping filter: '" + registration.getName() + "' to: "
+ Arrays.asList(DEFAULT_URL_MAPPINGS));
//重點(diǎn)關(guān)注此處
registration.addMappingForUrlPatterns(dispatcherTypes, this.matchAfter,
DEFAULT_URL_MAPPINGS);
}
else {
if (!servletNames.isEmpty()) {
this.logger.info("Mapping filter: '" + registration.getName()
+ "' to servlets: " + servletNames);
registration.addMappingForServletNames(dispatcherTypes, this.matchAfter,
StringUtils.toStringArray(servletNames));
}
if (!this.urlPatterns.isEmpty()) {
this.logger.info("Mapping filter: '" + registration.getName()
+ "' to urls: " + this.urlPatterns);
registration.addMappingForUrlPatterns(dispatcherTypes, this.matchAfter,
StringUtils.toStringArray(this.urlPatterns));
}
}
}
?對(duì)TomcatEmbeddedContext類的filterMap屬性進(jìn)行賦值供后面調(diào)用。filterMap里放入的FilterName是springSecurityFilterChain。文章來源地址http://www.zghlxwxcb.cn/news/detail-780355.html
public void addMappingForUrlPatterns(
EnumSet<DispatcherType> dispatcherTypes, boolean isMatchAfter,
String... urlPatterns) {
FilterMap filterMap = new FilterMap();
//filterDef 是 ApplicationContext里賦值的
filterMap.setFilterName(filterDef.getFilterName());
if (dispatcherTypes != null) {
for (DispatcherType dispatcherType : dispatcherTypes) {
filterMap.setDispatcher(dispatcherType.name());
}
}
if (urlPatterns != null) {
// % decoded (if necessary) using UTF-8
for (String urlPattern : urlPatterns) {
filterMap.addURLPattern(urlPattern);
}
if (isMatchAfter) {
context.addFilterMap(filterMap);
} else {
context.addFilterMapBefore(filterMap);
}
}
// else error?
}
到了這里,關(guān)于SpringSecurity源碼分析(一) SpringBoot集成SpringSecurity即Spring安全框架的加載過程的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!