springSecurity之AuthenticationProvider用法解析
AuthenticationProvider解析
首先進(jìn)入到AuthenticationProvider源碼中可以看到它只是個(gè)簡(jiǎn)單的接口里面也只有兩個(gè)方法:
public interface AuthenticationProvider { // 具體認(rèn)證流程 Authentication authenticate(Authentication authentication) throws AuthenticationException; // supports函數(shù)用來指明該P(yáng)rovider是否適用于該類型的認(rèn)證,如果不合適,則尋找另一個(gè)Provider進(jìn)行驗(yàn)證處理。 boolean supports(Class<?> authentication); }
AuthenticationProvider是用戶自定義身份認(rèn)證,認(rèn)證流程頂級(jí)接口。
唯一作用即使用來進(jìn)行身份驗(yàn)證,同時(shí)springSecurity也為我們提供了很多方便的實(shí)現(xiàn)類。
當(dāng)我們沒有指定相關(guān)AuthenticationProvider 對(duì)象時(shí)springSecurity默認(rèn)使用的就時(shí)上圖中的DaoAuthenticationProvider進(jìn)行驗(yàn)證。
也就是最常見的賬戶名密碼的方式。但是實(shí)際開發(fā)中我們往往需要實(shí)現(xiàn)自定義認(rèn)證流程比如最常見的短信驗(yàn)證碼,第三方登錄等等。
這個(gè)時(shí)候我們就可以通過實(shí)現(xiàn)自己的 AuthenticationProvider方式來進(jìn)行自定義認(rèn)證。
只需要在WebSecurityConfigurerAdapter適配器類的config方法中加入自己實(shí)現(xiàn)的AuthenticationProvider 即可。
@Override protected void configure(AuthenticationManagerBuilder auth) { auth.authenticationProvider(authenticationProvider()); }
說到AuthenticationProvider就離不開AuthenticationManager這個(gè)接口
public interface AuthenticationManager { Authentication authenticate(Authentication authentication) throws AuthenticationException; }
同樣的AuthenticationManager也是一個(gè)頂級(jí)接口,可以看到它其中也定義了一個(gè)跟AuthenticationProvider一摸一樣的方法。
如果說Auth ntic ationProvider是對(duì)認(rèn)證的具體實(shí)現(xiàn),則AuthenticationManager則是對(duì)我們眾多AuthenticationProvider的一個(gè)統(tǒng)一管理。
Authentication Ma nager的實(shí)現(xiàn)有很多,通常使用ProviderManager對(duì)認(rèn)證請(qǐng)求鏈進(jìn)行管理。
從源碼中可以看到
ProviderManager提供了一個(gè)list對(duì)AuthenticationProvider進(jìn)行統(tǒng)一管理,即一個(gè)認(rèn)證處理器鏈來支持同一個(gè)應(yīng)用中的多個(gè)不同身份認(rèn)證機(jī)制,ProviderManager將會(huì)根據(jù)順序來進(jìn)行驗(yàn)證。
public Authentication authenticate(Authentication authentication) throws AuthenticationException { Class<? extends Authentication> toTest = authentication.getClass(); AuthenticationException lastException = null; AuthenticationException parentException = null; Authentication result = null; Authentication parentResult = null; boolean debug = logger.isDebugEnabled(); for (AuthenticationProvider provider : getProviders()) { // 如果支持認(rèn)證實(shí)現(xiàn)類就繼續(xù)處理 if (!provider.supports(toTest)) { continue; } if (debug) { logger.debug("Authentication attempt using " + provider.getClass().getName()); } try { // 調(diào)用實(shí)現(xiàn)類的authenticate方法進(jìn)行真實(shí)業(yè)務(wù)邏輯認(rèn)證處理 result = provider.authenticate(authentication); if (result != null) { copyDetails(authentication, result); break; } } catch (AccountStatusException e) { prepareException(e, authentication); // SEC-546: Avoid polling additional providers if auth failure is due to // invalid account status throw e; } catch (InternalAuthenticationServiceException e) { prepareException(e, authentication); throw e; } catch (AuthenticationException e) { lastException = e; } } if (result == null && parent != null) { // Allow the parent to try. try { result = parentResult = parent.authenticate(authentication); } catch (ProviderNotFoundException e) { // ignore as we will throw below if no other exception occurred prior to // calling parent and the parent // may throw ProviderNotFound even though a provider in the child already // handled the request } catch (AuthenticationException e) { lastException = parentException = e; } } if (result != null) { if (eraseCredentialsAfterAuthentication && (result instanceof CredentialsContainer)) { // Authentication is complete. Remove credentials and other secret data // from authentication ((CredentialsContainer) result).eraseCredentials(); } // If the parent AuthenticationManager was attempted and successful than it will publish an AuthenticationSuccessEvent // This check prevents a duplicate AuthenticationSuccessEvent if the parent AuthenticationManager already published it if (parentResult == null) { // //發(fā)送認(rèn)證成功事件 eventPublisher.publishAuthenticationSuccess(result); } return result; } // Parent was null, or didn't authenticate (or throw an exception). if (lastException == null) { lastException = new ProviderNotFoundException(messages.getMessage( "ProviderManager.providerNotFound", new Object[] { toTest.getName() }, "No AuthenticationProvider found for {0}")); } // If the parent AuthenticationManager was attempted and failed than it will publish an AbstractAuthenticationFailureEvent // This check prevents a duplicate AbstractAuthenticationFailureEvent if the parent AuthenticationManager already published it if (parentException == null) { prepareException(lastException, authentication); } throw lastException; }
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
深入理解Java中的HashMap的實(shí)現(xiàn)機(jī)制
這篇文章主要介紹了深入理解Java中的HashMap的實(shí)現(xiàn)機(jī)制,同時(shí)也有助于理解Java中對(duì)于哈希函數(shù)的相關(guān)處理方式,需要的朋友可以參考下2015-07-07Springboot整合freemarker和相應(yīng)的語法詳解
FreeMarker是一款Spring官方推薦使用的模板引擎。接下來通過本文給大家介紹Springboot整合freemarker和相應(yīng)的語法,感興趣的朋友一起看看吧2021-09-09SpringBoot?Http遠(yuǎn)程調(diào)用的方法
這篇文章主要為大家詳細(xì)介紹了SpringBoot?Http遠(yuǎn)程調(diào)用的方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-08-08