有時需要使用AuthenticationManager(以下簡稱Manager)物件,可是這個物件不是Bean,沒有直接保存在Spring的Bean庫中,那么如何獲取Spring Security中的這個物件呢?
在Spring Security 5.7.0-M2之前,通常會擴展WebSecurityConfigurerAdapter(以下簡稱Adapter)類來自定義網路安全配置,Adapter類中有一個方法authenticationManager()可以提供Manager物件,但是從Spring Security 5.7.0-M2開始,Adapter類就被棄用,再用此類中的authenticationManager()方法獲取Manager物件就不合適了,
以下是Adapter#authenticationManager()方法的原始碼,
protected AuthenticationManager authenticationManager() throws Exception { if (!this.authenticationManagerInitialized) { configure(this.localConfigureAuthenticationBldr); if (this.disableLocalConfigureAuthenticationBldr) { this.authenticationManager = this.authenticationConfiguration.getAuthenticationManager(); } else { this.authenticationManager = this.localConfigureAuthenticationBldr.build(); } this.authenticationManagerInitialized = true; } return this.authenticationManager; }
可以發現在該方法中使用Adapter類的私有欄位authenticationConfiguration的getAuthenticationManager()方法獲取Manager物件,而欄位authenticationConfiguration是使用方法setAthenticationConfiguration()自動注入的,所以Spring的Bean庫中存在Manager物件,由此可得到如下獲取AuthenticationManager物件的方案一,
方案一:從Spring的Bean庫中獲取AuthenticationConfiguration(以下簡稱Configuration)物件,然后使用Configuration物件的getAuthenticationManager()方法獲取Manager物件,(關于如何從Spring中獲取Bean,本文不作介紹,請讀者自行查閱資料了解)
繼續查看Configuration#getAuthenticationManager()方法的原始碼,
public AuthenticationManager getAuthenticationManager() throws Exception { if (this.authenticationManagerInitialized) { return this.authenticationManager; } AuthenticationManagerBuilder authBuilder = this.applicationContext.getBean(AuthenticationManagerBuilder.class); if (this.buildingAuthenticationManager.getAndSet(true)) { return new AuthenticationManagerDelegator(authBuilder); } for (GlobalAuthenticationConfigurerAdapter config : this.globalAuthConfigurers) { authBuilder.apply(config); } this.authenticationManager = authBuilder.build(); if (this.authenticationManager == null) { this.authenticationManager = getAuthenticationManagerBean(); } this.authenticationManagerInitialized = true; return this.authenticationManager; }
原始碼中展示的流程如下,
① 如果Configuration物件中已保存有Manager物件,則回傳該物件,
② 如果Configuration物件中沒有Manager物件,則從Spring的Bean庫中獲取AuthenticationManagerBuilder(以下簡稱Builder)物件,
③ 如果已經用Builder物件構建了Manager物件,則回傳一個使用Builder物件初始化的AuthenticationManagerDelegator物件,
④ AuthenticationManagerDelegator是Manager的子類,該類代理了另一個Manager物件,被代理的Manager物件是使用Builder物件的getObject()方法獲得的,Builder#getObject()的代碼表明,在呼叫該方法前,需要先在Builder物件內構建一個Manager,也就是說可以從Spring的Bean庫中獲取Builder物件,然后用它的getObject()方法獲得Manager物件,
@Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { if (this.delegate != null) { return this.delegate.authenticate(authentication); } synchronized (this.delegateMonitor) { if (this.delegate == null) { this.delegate = this.delegateBuilder.getObject(); this.delegateBuilder = null; } } return this.delegate.authenticate(authentication); }
⑤ 如果尚未使用Builder物件構建Manager物件,則先把Configuration的私有欄位globalAuthConfigurers的資料應用在Builder物件上(私有欄位globalAuthConfigurers是通過方法setGlobalAuthConfigurers()自動注入的),
⑥ 然后使用Builder物件的build()方法構建Manager物件,
⑦ 在Builder#build()方法中,第一次呼叫該方法會使用doBuild()方法生成Manager物件(不分析doBuild()方法),并將這個物件快取下來,以后再呼叫build()方法將會拋出AlreadyBuiltException例外,也就是說可以從Spring的Bean庫中獲取Builder物件,然后用它的build()方法獲得Manager物件,
@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"); }
⑧ 如果Builder物件未能成功構建Manager物件,則使用Configuration的私有方法lazyBean()方法獲取Manager物件(不分析lazyBean()方法),
上述流程表明,可以從Spring的Bean庫中獲取AuthenticationManagerBuilder物件,然后使用該物件的build()方法或getObject()方法獲取AuthenticationManager物件,獲取AuthenticationManager物件的方案二如下,
方案二:從Spring的Bean庫中獲取AuthenticationManagerBuilder物件,首先呼叫該物件的build()方法獲取Manager物件,并對方法呼叫捕獲AlreadyBuiltException例外,若捕獲到例外,則在例外處理代碼中呼叫Builder物件的getObject()方法獲取Manager物件,
方案二可能有缺陷,在Configuration#getAuthenticationManager()方法的原始碼中可以看到步驟⑤對Builder物件物件做了處理,而方案二并沒有做這種處理,推薦使用方案一,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/538326.html
標籤:其他
上一篇:Java學習五
