springSecurity之AuthenticationProvider用法解析
AuthenticationProvider解析
首先進(jìn)入到AuthenticationProvider源碼中可以看到它只是個(gè)簡(jiǎn)單的接口里面也只有兩個(gè)方法:
public interface AuthenticationProvider {
// 具體認(rèn)證流程
Authentication authenticate(Authentication authentication)
throws AuthenticationException;
// supports函數(shù)用來(lái)指明該P(yáng)rovider是否適用于該類(lèi)型的認(rèn)證,如果不合適,則尋找另一個(gè)Provider進(jìn)行驗(yàn)證處理。
boolean supports(Class<?> authentication);
}
AuthenticationProvider是用戶自定義身份認(rèn)證,認(rèn)證流程頂級(jí)接口。
唯一作用即使用來(lái)進(jìn)行身份驗(yàn)證,同時(shí)springSecurity也為我們提供了很多方便的實(shí)現(xiàn)類(lèi)。

當(dāng)我們沒(méi)有指定相關(guān)AuthenticationProvider 對(duì)象時(shí)springSecurity默認(rèn)使用的就時(shí)上圖中的DaoAuthenticationProvider進(jìn)行驗(yàn)證。
也就是最常見(jiàn)的賬戶名密碼的方式。但是實(shí)際開(kāi)發(fā)中我們往往需要實(shí)現(xiàn)自定義認(rèn)證流程比如最常見(jiàn)的短信驗(yàn)證碼,第三方登錄等等。
這個(gè)時(shí)候我們就可以通過(guò)實(shí)現(xiàn)自己的 AuthenticationProvider方式來(lái)進(jìn)行自定義認(rèn)證。
只需要在WebSecurityConfigurerAdapter適配器類(lèi)的config方法中加入自己實(shí)現(xiàn)的AuthenticationProvider 即可。
@Override
protected void configure(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(authenticationProvider());
}
說(shuō)到AuthenticationProvider就離不開(kāi)AuthenticationManager這個(gè)接口
public interface AuthenticationManager {
Authentication authenticate(Authentication authentication)
throws AuthenticationException;
}
同樣的AuthenticationManager也是一個(gè)頂級(jí)接口,可以看到它其中也定義了一個(gè)跟AuthenticationProvider一摸一樣的方法。
如果說(shuō)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)證處理器鏈來(lái)支持同一個(gè)應(yīng)用中的多個(gè)不同身份認(rèn)證機(jī)制,ProviderManager將會(huì)根據(jù)順序來(lái)進(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)類(lèi)就繼續(xù)處理
if (!provider.supports(toTest)) {
continue;
}
if (debug) {
logger.debug("Authentication attempt using "
+ provider.getClass().getName());
}
try {
// 調(diào)用實(shí)現(xiàn)類(lèi)的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)文章
javascript與jsp發(fā)送請(qǐng)求到servlet的幾種方式實(shí)例
本文分別給出了javascript發(fā)送請(qǐng)求到servlet的5種方式實(shí)例與 jsp發(fā)送請(qǐng)求到servlet的6種方式實(shí)例2018-03-03
Feign 集成 Hystrix實(shí)現(xiàn)不同的調(diào)用接口不同的設(shè)置方式
這篇文章主要介紹了Feign 集成 Hystrix實(shí)現(xiàn)不同的調(diào)用接口不同的設(shè)置方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06
Java中@DateTimeFormat注解與@JsonFormat注解的使用方式
這篇文章主要介紹了Java中@DateTimeFormat注解與@JsonFormat注解的使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08
Java Ehcache緩存框架入門(mén)級(jí)使用實(shí)例
這篇文章主要介紹了Java Ehcache緩存框架入門(mén)級(jí)使用實(shí)例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-08-08
Spring Validation方法實(shí)現(xiàn)原理分析
這篇文章主要介紹了Spring Validation實(shí)現(xiàn)原理分析,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-07-07
Spring data jpa的使用與詳解(復(fù)雜動(dòng)態(tài)查詢及分頁(yè),排序)
這篇文章主要介紹了Spring data jpa的使用與詳解(復(fù)雜動(dòng)態(tài)查詢及分頁(yè),排序),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11
解決springboot集成rocketmq關(guān)于tag的坑
這篇文章主要介紹了解決springboot集成rocketmq關(guān)于tag的坑,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08

