1. 前言
我們對 AuthenticationManager 的初始化的細節(jié)進行了分析雁刷,其中里面有一段代碼引起了不少同學的注意:
ApplicationContext context = http.getSharedObject(ApplicationContext.class);
CaptchaAuthenticationProvider captchaAuthenticationProvider = context.getBean("captchaAuthenticationProvider", CaptchaAuthenticationProvider.class);
上面直接從 HttpSecurity 對象中獲取到 Spring 的應用上下文對象 ApplicationContext币绩,它是怎么做到的呢枫吧? SharedObject 又是個什么概念粱哼?今天就來搞清楚這個問題锨并。
2. SharedObject
在 Spring Security 中 SharedObject 既不是對象也不是接口狰闪,而是某一類“可共享”的對象的統(tǒng)稱辛润。
顧名思義永淌, SharedObject 的意思是可共享的對象崎场。它的作用是如果一些對象你希望在不同的作用域配置中共享它們就把這些對象變成 SharedObject ,有點分布式對象的感覺遂蛀。為了更加便于你理解谭跨,下面是相關(guān)的體系結(jié)構(gòu):
AbstractConfiguredSecurityBuilder 或者 HttpSecurityBuilder 的實現(xiàn)類才具有操作SharedObject 的能力。一種是注冊 SharedObject 李滴,另一種是獲取 SharedObject 螃宙。
SharedObject的注冊
SharedObject會以其 Class 類型為 Key ,實例為 Value 存儲到一個 HashMap<Class<?>,Object> 中,具體可看 HttpSecurity 源碼所坯。它的注冊分為兩個部分谆扎,第一是 HttpSecurity初始化的時候裝配進去的。我們來看看:
我們熟知的 AuthenticationManagerBuilder 在這里被共享包竹。
還有一部分是在所有的 HttpSecurityBuilder 對象初始化時注冊的燕酷。它初始化和配置都是由 SecurityConfigurer 來完成的:
public interface SecurityConfigurer<O, B extends SecurityBuilder<O>> {
void init(B builder) throws Exception;
void configure(B builder) throws Exception;
}
上面兩個方法分別用來初始化和配置 HttpSecurityBuilder 。比如我們熟知的WebSecurityConfigurerAdapter 就是用來配置 HttpSecurity 的周瞎,在其 init 方法中我們可以找到相關(guān)的代碼:
private Map<Class<?>, Object> createSharedObjects() {
Map<Class<?>, Object> sharedObjects = new HashMap<>();
sharedObjects.putAll(localConfigureAuthenticationBldr.getSharedObjects());
sharedObjects.put(UserDetailsService.class, userDetailsService());
sharedObjects.put(ApplicationContext.class, context);
sharedObjects.put(ContentNegotiationStrategy.class, contentNegotiationStrategy);
sharedObjects.put(AuthenticationTrustResolver.class, trustResolver);
return sharedObjects;
}
這也是我在文章開頭可以獲取到 ApplicationContext 的根本原因苗缩。
SharedObject的獲取和使用
我們能獲取到哪些被標記為 SharedObject 類呢? SecurityConfigurer 有很多實現(xiàn)声诸,這些實現(xiàn)都是用來配置一些特定的同認證授權(quán)相關(guān)的功能的酱讶。比如 OAuth2ClientConfigurer 用來配置OAuth2客戶端的,它里面就將常用的一些對象設(shè)置為 SharedObject :
public OAuth2ClientConfigurer<B> clientRegistrationRepository(ClientRegistrationRepository clientRegistrationRepository) {
Assert.notNull(clientRegistrationRepository, "clientRegistrationRepository cannot be null");
this.getBuilder().setSharedObject(ClientRegistrationRepository.class, clientRegistrationRepository);
return this;
}
當你在 HttpSecurity 的配置中的其它地方需要用到 ClientRegistrationRepository 時彼乌,你可以直接通過 getSharedObject 獲取泻肯,就像文章開頭一樣,而不用在去寫一些獲取方法了慰照。