国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

SpringSecurity源碼分析(一) SpringBoot集成SpringSecurity即Spring安全框架的加載過程

這篇具有很好參考價(jià)值的文章主要介紹了SpringSecurity源碼分析(一) SpringBoot集成SpringSecurity即Spring安全框架的加載過程。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

? ? ? 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類型的過濾器。

[org/springframework/boot/autoconfigure/security/servlet/websecurityenablerc,SpringSecurity,spring boot,spring,java

下面我會(huì)詳細(xì)的分析這三個(gè)類和對(duì)象是怎么關(guān)聯(lián)起來?。

?1.1.2 安全框架的配置類及如何加載

? ? 在springboot啟動(dòng)過程中會(huì)獲取spring.factories配置文件里的配置類并加載到spring容器中,觀察spring.factories配置文件里的配置內(nèi)容,涉及到springsecurity的如下圖紅框處所示。[org/springframework/boot/autoconfigure/security/servlet/websecurityenablerc,SpringSecurity,spring boot,spring,java

? ? ? 我們首先關(guān)注SecurityAutoConfiguration和SecurityFilterAutoConfiguration這兩個(gè)配置類。

深入分析這些類,我們可以看到配置類的關(guān)系如下圖所示:

[org/springframework/boot/autoconfigure/security/servlet/websecurityenablerc,SpringSecurity,spring boot,spring,java

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)用該方法。

[org/springframework/boot/autoconfigure/security/servlet/websecurityenablerc,SpringSecurity,spring boot,spring,java在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);

	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)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 【框架源碼】SpringBoot核心源碼解讀之啟動(dòng)類源碼分析

    【框架源碼】SpringBoot核心源碼解讀之啟動(dòng)類源碼分析

    首先我們要先帶著我們的疑問,spring boot是如何啟動(dòng)應(yīng)用程序?去分析SpringBoot的啟動(dòng)源碼。 我們?cè)谛陆⊿pringBoot項(xiàng)目時(shí),核心方法就是主類的run方法。 SpringApplication.run(ArchWebApplication.class, args) 我們點(diǎn)擊run方法進(jìn)入到源碼中,這塊傳入的了一個(gè)我們當(dāng)前程序主類的類對(duì)象以及主

    2024年02月06日
    瀏覽(23)
  • 權(quán)限管理 springboot集成springSecurity Oauth2 JWT

    權(quán)限管理 springboot集成springSecurity Oauth2 JWT

    目錄 一、SpringSeurity的基礎(chǔ)操作 1、引入主要依賴 2、加密器 3、實(shí)現(xiàn)自定義登錄邏輯 4、訪問限制 5、自定義異常處理? 6、通過注解的方式配置訪問控制 二、Auth2認(rèn)證方案 1、什么是Auth2認(rèn)證 2、Oauth2最常用的授權(quán)模式? 3、依賴引入 4、添加配置類 5、測(cè)試 6、存在到Redis里,后續(xù)

    2023年04月14日
    瀏覽(27)
  • SpringSecurity安全框架簡(jiǎn)介

    Spring Security是Spring全家桶的成員,官方對(duì)它的介紹是: 從介紹里可以看出,Spring Security是一個(gè)可定制擴(kuò)展的框架,它主要提供了身份驗(yàn)證和訪問控制功能。而這兩個(gè)功能也是基于框架的擴(kuò)展機(jī)制開發(fā)的,下面讓我們一起了解一下Spring Security的基本概念和擴(kuò)展機(jī)制的實(shí)現(xiàn)原理。

    2024年02月08日
    瀏覽(20)
  • SpringSecurity安全框架

    SpringSecurity安全框架

    我們使用這個(gè)springSecurity安全框架,作用是認(rèn)證,授權(quán),將 用戶的權(quán)限和對(duì)應(yīng)的資源進(jìn)行綁定 , 默認(rèn)的是在內(nèi)存中保存的,實(shí)際開發(fā)中,是需要根據(jù)項(xiàng)目業(yè)務(wù)的需求 對(duì)某些方法進(jìn)行重寫, 使數(shù)據(jù)庫中權(quán)限對(duì)應(yīng)的資源進(jìn)行綁定, 就是查看當(dāng)前登錄的用戶所扮演的角色,該角色有哪些權(quán)限

    2024年02月22日
    瀏覽(18)
  • SpringSecurity 安全框架詳解

    SpringSecurity 安全框架詳解

    先贅述一下身份認(rèn)證和用戶授權(quán): 用戶認(rèn)證( Authentication ):系統(tǒng)通過校驗(yàn)用戶提供的用戶名和密碼來驗(yàn)證該用戶是否為系統(tǒng)中的合法主體,即是否可以訪問該系統(tǒng); 用戶授權(quán)( Authorization ):系統(tǒng)為用戶分配不同的角色,以獲取對(duì)應(yīng)的權(quán)限,即驗(yàn)證該用戶是否有權(quán)限執(zhí)行

    2024年02月02日
    瀏覽(15)
  • SpringBoot原理分析 | 安全框架:Security

    SpringBoot原理分析 | 安全框架:Security

    ??wei_shuo的個(gè)人主頁 ??wei_shuo的學(xué)習(xí)社區(qū) ??Hello World ! Spring Security是一個(gè)能夠?yàn)榛赟pring的企業(yè)應(yīng)用系統(tǒng)提供聲明式的安全訪問控制解決方案的安全框架;提供一組可以在Spring應(yīng)用上下文中配置的Bean,充分利用Spring IoC,DI(控制反轉(zhuǎn)Inversion of Control ,DI:Dependency Injection

    2024年02月13日
    瀏覽(20)
  • 【SpringBoot】簡(jiǎn)介及傳統(tǒng)的 Spring 框架:對(duì)比和分析

    【SpringBoot】簡(jiǎn)介及傳統(tǒng)的 Spring 框架:對(duì)比和分析

    ?哈嘍,哈嘍,大家好~ 我是你們的老朋友: 保護(hù)小周?? ? 今天給大家?guī)淼氖?SpringBoot 的簡(jiǎn)介,SpringBoot 項(xiàng)目的創(chuàng)建,相較于 Spring 框架的優(yōu)點(diǎn): 1. 快速的集成框架? 2.內(nèi)置運(yùn)行容器, 快速的部署項(xiàng)目?3. 摒棄繁瑣的 xml,使用注解和配置的方式進(jìn)行開發(fā)。4. 支持更多的監(jiān)控

    2024年02月15日
    瀏覽(22)
  • SpringSecurity分布式安全框架

    SpringSecurity分布式安全框架

    Spring Security是一個(gè)基于Spring框架的安全框架,它提供了全面的安全解決方案,包括用戶認(rèn)證和用戶授權(quán)等Web應(yīng)用安全性問題。Spring Security可以輕松擴(kuò)展以滿足自定義需求,它的真正強(qiáng)大之處在于它可以輕松擴(kuò)展以滿足自定義要求。 對(duì)于分布式系統(tǒng)來說,Spring Security可以結(jié)合

    2024年02月08日
    瀏覽(24)
  • SpringSecurity安全框架 ——認(rèn)證與授權(quán)

    SpringSecurity安全框架 ——認(rèn)證與授權(quán)

    目錄 ?一、簡(jiǎn)介 1.1 什么是Spring Security 1.2?工作原理 1.3?為什么選擇Spring Security 1.4?HttpSecurity 介紹?? 二、用戶認(rèn)證 2.1 導(dǎo)入依賴與配置 2.2?用戶對(duì)象UserDetails 2.3?業(yè)務(wù)對(duì)象UserDetailsService 2.4 SecurityConfig配置 2.4.1?BCryptPasswordEncoder密碼編碼器 2.4.2?RememberMe 記住登錄信息 2.4.3?CSR

    2024年02月04日
    瀏覽(19)
  • springcloud/springboot集成NACOS 做注冊(cè)和配置中心以及nacos源碼分析

    springcloud/springboot集成NACOS 做注冊(cè)和配置中心以及nacos源碼分析

    Spring Cloud 是一系列框架的有序集合如服務(wù)發(fā)現(xiàn)注冊(cè)、配置中心、消息總線、負(fù)載均衡、熔斷器、數(shù)據(jù)監(jiān)控等。 SpringCloud 將多個(gè)服務(wù)框架組合起來,通過Spring Boot進(jìn)行再封裝,屏蔽掉了復(fù)雜的配置和實(shí)現(xiàn)原理,最終給開發(fā)者提供了一套簡(jiǎn)單易懂、易部署和易維護(hù)的分布式系統(tǒng)開

    2024年02月08日
    瀏覽(97)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包