Spring Boot Security原理一(八)

1 概述

默認Springboot Security安全配置自動加載在SecurityAutoConfiguration和UserDetailsServiceAutoConfiguration。
具體請看spring-boot-autoconfigure-2.0.7.RELEASE.jar下的/META-INF/spring.factories文件
在這裏插入圖片描述

SecurityAutoConfiguration類:導入SpringBootWebSecurityConfiguration攔截響應的請求;UserDetailsServiceAutoConfiguration類:配置身份驗證。

@Configuration
@ConditionalOnClass(DefaultAuthenticationEventPublisher.class)
@EnableConfigurationProperties(SecurityProperties.class)
@Import({ SpringBootWebSecurityConfiguration.class, WebSecurityEnablerConfiguration.class,
		SecurityDataConfiguration.class })
public class SecurityAutoConfiguration {

	@Bean
	@ConditionalOnMissingBean(AuthenticationEventPublisher.class)
	public DefaultAuthenticationEventPublisher authenticationEventPublisher(
			ApplicationEventPublisher publisher) {
		return new DefaultAuthenticationEventPublisher(publisher);
	}

}
@Configuration
@ConditionalOnClass(AuthenticationManager.class)
@ConditionalOnBean(ObjectPostProcessor.class)
// 沒有AuthenticationManager、AuthenticationProvider和UserDetailsService,加載該bean配置系想你
@ConditionalOnMissingBean({ AuthenticationManager.class, AuthenticationProvider.class,
		UserDetailsService.class })
public class UserDetailsServiceAutoConfiguration {
	....省略
}

1.1 關閉默認Web應用程序安全配置

如果要完全關閉默認Web應用程序安全配置,您可以添加bean繼承 WebSecurityConfigurerAdapter,這樣Springboot將不會初始化默認的WebSecurityConfigurerAdapter (這樣做不會禁用UserDetailsService配置或Actuator的安全性)。
SpringBootWebSecurityConfiguration 的代碼如下:

@Configuration
@ConditionalOnClass(WebSecurityConfigurerAdapter.class)
// 沒有WebSecurityConfigurerAdapter類或子類加載默認配置
@ConditionalOnMissingBean(WebSecurityConfigurerAdapter.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
public class SpringBootWebSecurityConfiguration {

	@Configuration
	@Order(SecurityProperties.BASIC_AUTH_ORDER)
	static class DefaultConfigurerAdapter extends WebSecurityConfigurerAdapter {

	}
}

關閉UserDetailsService的配置,您可以添加類繼承 UserDetailsService,AuthenticationProvider或AuthenticationManager,來覆蓋Springboot默認實現。
UserDetailsServiceAutoConfiguration 代碼如下:

@Configuration
@ConditionalOnClass(AuthenticationManager.class)
@ConditionalOnBean(ObjectPostProcessor.class)
// 沒有AuthenticationManager、AuthenticationProvider和UserDetailsService,加載該bean配置系想你
@ConditionalOnMissingBean({ AuthenticationManager.class, AuthenticationProvider.class,
		UserDetailsService.class })
public class UserDetailsServiceAutoConfiguration {

	@Bean
	@ConditionalOnMissingBean(type = "org.springframework.security.oauth2.client.registration.ClientRegistrationRepository")
	@Lazy
	public InMemoryUserDetailsManager inMemoryUserDetailsManager(
			SecurityProperties properties,
			ObjectProvider<PasswordEncoder> passwordEncoder) {
		SecurityProperties.User user = properties.getUser();
		List<String> roles = user.getRoles();
		return new InMemoryUserDetailsManager(User.withUsername(user.getName())
				.password(getOrDeducePassword(user, passwordEncoder.getIfAvailable()))
				.roles(StringUtils.toStringArray(roles)).build());
	}
}

2 流程分析

經過上面分析我們知道怎麼關閉Springboot默認實現,接着我們看SecurityAutoConfiguration 類中導入的WebSecurityEnablerConfiguration配置信息

@Configuration
@ConditionalOnBean(WebSecurityConfigurerAdapter.class)
@ConditionalOnMissingBean(name = BeanIds.SPRING_SECURITY_FILTER_CHAIN)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@EnableWebSecurity
public class WebSecurityEnablerConfiguration {

}
@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配置了Security關鍵信息

@Configuration
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
	private WebSecurity webSecurity;
	private Boolean debugEnabled;
	private List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers;
	private ClassLoader beanClassLoader;
	@Autowired(required = false)
	private ObjectPostProcessor<Object> objectObjectPostProcessor;

	......省略
		
	@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
	public Filter springSecurityFilterChain() throws Exception {
		boolean hasConfigurers = webSecurityConfigurers != null
				&& !webSecurityConfigurers.isEmpty();
		if (!hasConfigurers) {
			WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor
					.postProcess(new WebSecurityConfigurerAdapter() {
					});
			webSecurity.apply(adapter);
		}
		return webSecurity.build();
	}


	@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);
		}

		Collections.sort(webSecurityConfigurers, AnnotationAwareOrderComparator.INSTANCE);

		Integer previousOrder = null;
		Object previousConfig = null;
		for (SecurityConfigurer<Filter, WebSecurity> config : webSecurityConfigurers) {
			Integer order = AnnotationAwareOrderComparator.lookupOrder(config);
			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);
		}
		this.webSecurityConfigurers = webSecurityConfigurers;
	}

	......省略
}

2.1 setFilterChainProxySecurityConfigurer()方法

開始時會將所有實現SecurityConfigurer接口的類注入到webSecurityConfigurers參數中

@Autowired(required = false)
	public void setFilterChainProxySecurityConfigurer(
			ObjectPostProcessor<Object> objectPostProcessor,
			@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
			throws Exception {
			// 創建webSecurity對象 ,應爲是new來的對象,沒有走Spring的生命週期,可以利用objectPostProcessor讓new來的對象走Spring的生命週期
		webSecurity = objectPostProcessor
				.postProcess(new WebSecurity(objectPostProcessor));
		if (debugEnabled != null) {
			webSecurity.debug(debugEnabled);
		}

		// 主要檢驗了配置實例的order順序
		Collections.sort(webSecurityConfigurers, AnnotationAwareOrderComparator.INSTANCE);

		Integer previousOrder = null;
		Object previousConfig = null;
		for (SecurityConfigurer<Filter, WebSecurity> config : webSecurityConfigurers) {
			Integer order = AnnotationAwareOrderComparator.lookupOrder(config);
			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對象中,其中配置實例中含有我們自定義業務的權限控制配置信息
			webSecurity.apply(webSecurityConfigurer);
		}
		this.webSecurityConfigurers = webSecurityConfigurers;
	}

該方法中 主要執行如下

 1、創建webSecurity對象

 2、主要檢驗了配置實例的order順序(order唯一 否則會報錯)

 3、將所有的配置實例存放進入到webSecurity對象中,其中配置實例中含有我們自定義業務的權限控制配置信息

2.2 springSecurityFilterChain()方法

@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
	public Filter springSecurityFilterChain() throws Exception {
		boolean hasConfigurers = webSecurityConfigurers != null
				&& !webSecurityConfigurers.isEmpty();
		// 這個方法會判斷我們上一個方法中有沒有獲取到webSecurityConfigurers,沒有的話這邊會創建一個WebSecurityConfigurerAdapter實例,並追加到websecurity中
		if (!hasConfigurers) {
			WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor
					.postProcess(new WebSecurityConfigurerAdapter() {
					});
			webSecurity.apply(adapter);
		}
		// 調用websecurity的build方法
		return webSecurity.build();
	}

該方法中 主要執行如下

 1、這個方法會判斷我們上一個方法中有沒有獲取到webSecurityConfigurers,沒有的話這邊會創建一個WebSecurityConfigurerAdapter實例,並追加到websecurity中

 2、調用websecurity的build方法

2.2.1 websecurity的build()方法

SecurityBuilder繼承關係:
在這裏插入圖片描述
AbstractSecurityBuilder類build()方法

public abstract class AbstractSecurityBuilder<O> implements SecurityBuilder<O> {
	private AtomicBoolean building = new AtomicBoolean();
	private O object;
	
	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()方法

public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBuilder<O>>
		extends AbstractSecurityBuilder<O> {
	@Override
	protected final O doBuild() throws Exception {
		synchronized (configurers) {
			buildState = BuildState.INITIALIZING;

			beforeInit();
			init();

			buildState = BuildState.CONFIGURING;

			beforeConfigure();
			configure();

			buildState = BuildState.BUILDING;

			O result = performBuild();

			buildState = BuildState.BUILT;

			return result;
		}
	}
	
	protected void beforeInit() throws Exception {
	}
	
	@SuppressWarnings("unchecked")
	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);
		}
	}

	protected void beforeConfigure() throws Exception {
	}
	
 	@SuppressWarnings("unchecked")
	private void configure() throws Exception {
		Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();

		for (SecurityConfigurer<O, B> configurer : configurers) {
			configurer.configure((B) this);
		}
	}
	
	......省略
}

build過程主要分三步,init->configure->peformBuild ,具體流程圖在這裏插入圖片描述

2.2.1.1 init方法
 1、調用getHttp()方法獲取一個http實例,getHttp()方法
	 Ⅰ、添加認證的事件的發佈者
	 Ⅱ、配置認證管理器,會調用我們的繼承的WebSecurityConfigurerAdapter中重寫的
	 		configure(AuthenticationManagerBuilder auth)方法,配置認證相關信息
	 Ⅲ、追加各種SecurityConfigurer的具體實現類到httpSecurity中,如exceptionHandling()方法會追加一個
			ExceptionHandlingConfigurer,sessionManagement()方法會追加一個SessionManagementConfigurer,
	 		securityContext()方法會追加一個SecurityContextConfigurer對象,
	 		這些SecurityConfigurer的具體實現類最終會爲我們配置各種具體的filter
	 Ⅳ、最終調用我們的繼承的WebSecurityConfigurerAdapter中重寫的configure(HttpSecurity http),
	 		將我們業務相關的權限配置規則信息進行初始化操作。默認的configure(HttpSecurity http)方法繼續向httpSecurity類中追加	
	 		SecurityConfigurer的具體實現類,如authorizeRequests()方法追加一個ExpressionUrlAuthorizationConfigurer,
	 		formLogin()方法追加一個FormLoginConfigurer。 其中ExpressionUrlAuthorizationConfigurer這個實現類比較重要,
	 		因爲他會給我們創建一個非常重要的對象FilterSecurityInterceptor對象,FormLoginConfigurer對象比較簡單,
	 		但是也會爲我們提供一個在安全認證過程中經常用到會用的一個Filter:UsernamePasswordAuthenticationFilter。

2、通過web.addSecurityFilterChainBuilder方法把獲取到的HttpSecurity實例賦值給WebSecurity的securityFilterChainBuilders屬性

從init可以看出一個WebSecurity可以生成多個HttpSecurity,而在HttpSecurity又會創建多個攔截器,最終封裝到FilterChainProxy對象中
以上三個方法就是WebSecurityConfigurerAdapter類中init方法的主要邏輯,

@Order(100)
public abstract class WebSecurityConfigurerAdapter implements
		WebSecurityConfigurer<WebSecurity> {

	public void init(final WebSecurity web) throws Exception {
		final HttpSecurity http = getHttp();
		web.addSecurityFilterChainBuilder(http).postBuildAction(new Runnable() {
			public void run() {
				FilterSecurityInterceptor securityInterceptor = http
						.getSharedObject(FilterSecurityInterceptor.class);
				web.securityInterceptor(securityInterceptor);
			}
		});
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	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);
		authenticationBuilder.authenticationEventPublisher(eventPublisher);
		Map<Class<? extends Object>, Object> sharedObjects = createSharedObjects();

		http = new HttpSecurity(objectPostProcessor, authenticationBuilder,
				sharedObjects);
		if (!disableDefaults) {
			// @formatter:off
			// 追加各種SecurityConfigurer的具體實現類到httpSecurity中
			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();
			ClassLoader classLoader = this.context.getClassLoader();
			List<AbstractHttpConfigurer> defaultHttpConfigurers =
					SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader);

			for(AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
				http.apply(configurer);
			}
		}
		//最終調用我們的繼承的WebSecurityConfigurerAdapter中重寫的configure(),將我們業務相關的權限配置規則信息進行初始化操作
		configure(http);
		return http;
	}

	protected AuthenticationManager authenticationManager() throws Exception {
		if (!authenticationManagerInitialized) {
			configure(localConfigureAuthenticationBldr);
			if (disableLocalConfigureAuthenticationBldr) {
				authenticationManager = authenticationConfiguration
						.getAuthenticationManager();
			}
			else {
				authenticationManager = localConfigureAuthenticationBldr.build();
			}
			authenticationManagerInitialized = true;
		}
		return authenticationManager;
	}
}

2.2.1.2 configure方法

configure方法最終也調用到會我們繼承的WebSecurityConfigurerAdapter中重寫的configure(WebSecurity web)方法,默認實現中這個是一個空方法,具體應用中也經常重寫這個方法來實現特定需求。

2.2.1.3 peformBuild方法

具體的實現邏輯在WebSecurity類中

遍歷securityFilterChainBuilders屬性中的SecurityBuilder對象,並調用他的build方法。

這個securityFilterChainBuilders屬性我們前面也有提到過(具體請看2.2.1.1 init方法),就是在WebSecurityConfigurerAdapter類的init方法中獲取http後賦值給了WebSecurity。因此這個地方就是調用httpSecurity的build方法。
httpSecurity的build方式向其中追加一個個過濾器

@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屬性中的SecurityBuilder對象,並調用他的build方法。 
		for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {
			securityFilterChains.add(securityFilterChainBuilder.build());
		}
		// 構建攔截器鏈
		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);
		}
		postBuildAction.run();
		return result;
	}

2.2.2 Configurer轉換爲filter

peformBuild方法,會在webSecurity類調用httpSecurity的build(具體請看2.2.1.3 peformBuild方法)
httpSecurity也繼承於SecurityBuilder,因此也會走build過程,init->configure->peformBuil
在init的時候我們可以看到,會執行這些配置信息,這是在2.2.1.1 init方法中注入的,
在這裏插入圖片描述

在configure方法中,會調用所有的configures,調用該類中的configure方法初始化filter類

@SuppressWarnings("unchecked")
	private void configure() throws Exception {
		Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();

		for (SecurityConfigurer<O, B> configurer : configurers) {
			configurer.configure((B) this);
		}
	}

以CsfConfigurer爲例

public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
		extends AbstractHttpConfigurer<CsrfConfigurer<H>, H> {
	@SuppressWarnings("unchecked")
	@Override
	public void configure(H http) throws Exception {
	    // 創建Filter並配置
		CsrfFilter filter = new CsrfFilter(this.csrfTokenRepository);
		RequestMatcher requireCsrfProtectionMatcher = getRequireCsrfProtectionMatcher();
		if (requireCsrfProtectionMatcher != null) {
			filter.setRequireCsrfProtectionMatcher(requireCsrfProtectionMatcher);
		}
		AccessDeniedHandler accessDeniedHandler = createAccessDeniedHandler(http);
		if (accessDeniedHandler != null) {
			filter.setAccessDeniedHandler(accessDeniedHandler);
		}
		LogoutConfigurer<H> logoutConfigurer = http.getConfigurer(LogoutConfigurer.class);
		if (logoutConfigurer != null) {
			logoutConfigurer
					.addLogoutHandler(new CsrfLogoutHandler(this.csrfTokenRepository));
		}
		SessionManagementConfigurer<H> sessionConfigurer = http
				.getConfigurer(SessionManagementConfigurer.class);
		if (sessionConfigurer != null) {
			sessionConfigurer.addSessionAuthenticationStrategy(
					new CsrfAuthenticationStrategy(this.csrfTokenRepository));
		}
		filter = postProcess(filter);
		http.addFilter(filter);
	}
}

最後再看下HttpSecurity類執行build的最後一步 performBuild,這個方法就是在HttpSecurity中實現的


@Override  
    protected DefaultSecurityFilterChain performBuild() throws Exception {  
        Collections.sort(filters, comparator);  
        return new DefaultSecurityFilterChain(requestMatcher, filters);  
    }  

可以看到,這個類只是把我們追加到HttpSecurity中的security進行了排序,用的排序類是FilterComparator,從而保證我們的filter按照正確的順序執行。接着將filters構建成filterChian返回。在前面WebSecurity的performBuild方法中,這個返回值會被包裝成FilterChainProxy,並作爲WebSecurity的build方法的放回值。從而以springSecurityFilterChain這個名稱註冊到springContext中(在WebSecurityConfiguration中做的)

ExpressionUrlAuthorizationConfigurer的繼承關係
ExpressionUrlAuthorizationConfigurer->AbstractInterceptUrlConfigurer->AbstractHttpConfigurer->SecurityConfigurerAdapter->SecurityConfigurer
對應的init方法在SecurityConfigurerAdapter類中,是個空實現,什麼也沒有做,configure方法在SecurityConfigurerAdapter類中也有一個空實現,在AbstractInterceptUrlConfigurer類中進行了重寫

ExpressionUrlAuthorizationConfigurer類圖如下所示:
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章