在Acegi中是由認(rèn)證管理器確定用戶身份。一個(gè)認(rèn)證管理器由借口AuthenticationManager實(shí)現(xiàn)。
public
?
interface
?AuthenticationManager?
{
????
//
~Methods?==================================================================?
???

???
/**?*
/**
?????*?嘗試驗(yàn)證用戶身份,如果驗(yàn)證成功,將返回一個(gè)含授權(quán)的完整的Authentication對(duì)象。
?????*?一個(gè)AuthenticationManager必須按照下面的規(guī)則處理異常:
?????*?如果賬號(hào)是不被允許的必須拋出DisabledException,AuthenticationManager能檢測(cè)到這個(gè)狀態(tài)。
?????*?如果賬號(hào)已經(jīng)被鎖定必須拋出LockedException,AuthenticationManager能夠檢測(cè)被鎖定的賬號(hào)。
?????*?如果取到的是非法的信任狀(credential?)必須拋出BadCredentialsException。同時(shí)上述的2種異常也是可選
?????*?的,AuthenticationManager必須總是檢測(cè)信任狀(credential)。
?????*?異常必須被檢測(cè),如果有上述的異常出現(xiàn)須拋出異常。
?????*?如果一個(gè)賬號(hào)是不被允許的或是被鎖定的,認(rèn)證請(qǐng)求會(huì)立即被拒絕,信任狀的檢測(cè)不會(huì)發(fā)生。
?????*?這防止了對(duì)不被允許賬號(hào)和被鎖定的賬號(hào)的信任狀檢測(cè)。
?????*
?????*?
@param
?authentication?the?authentication?request?object
?????*
?????*?
@return
?a?fully?authenticated?object?including?credentials
?????*
?????*?
@throws
?AuthenticationException?if?authentication?fails
????
*/
????
public
?Authentication?authenticate(Authentication?authentication)
????????
throws
?AuthenticationException;
}
Authentication?繼承了java.security.Principal,Principal實(shí)現(xiàn)了簡(jiǎn)單的主體(Principal)定義。
public
?
interface
?Principal?
{

????
public
?
boolean
?equals(Object?another);

????
public
?String?toString();

????
public
?
int
?hashCode();


???
/**?*/
/**
?????*?返回主體的名字
?????*
?????*?
@return
?the?name?of?this?principal.
????
*/
????
public
?String?getName();
}
?
public
?
interface
?Authentication?
extends
?Principal,?Serializable?
{
????
//
?~Methods?==================================================================
?
/**?*/
/**
?????*?通過(guò)AuthenticationManager設(shè)置Principal的授權(quán)信息。
?????*?注意:當(dāng)Class狀態(tài)為valid時(shí),除非Value已經(jīng)被可信任的AuthenticationManager設(shè)置t過(guò),否則對(duì)Class
?????*?不起作用。(此句存疑?Note?that?classes?should?not?rely?on?this?value?as?being?valid?unless?it?has?
?????*?been?set?by?AuthenticationManager)
?????*?Authentication的實(shí)現(xiàn)類需要確保修改返回的數(shù)足不會(huì)影響Authentication?Object的狀態(tài)。(比如返回一個(gè)
?????*?數(shù)組的copy)。
?????*?
?????*?
@return
?the?authorities?granted?to?the?principal,?or?null?if?authentication?has?not?been?completed
????
*/
????
public
?GrantedAuthority[]?getAuthorities();


??
/**?*/
/**
?????*?信任狀證明Principal的身份是合法的。它經(jīng)常是密碼,但是也可以是任何與AuthenticationManager
?????*?相關(guān)的東西。
?????*
?????*?
@return
?the?credentials?that?prove?the?identity?of?the?Principal
????
*/
????
public
?Object?getCredentials();


??
/**?*/
/**
?????*?儲(chǔ)存認(rèn)證請(qǐng)求的額外信息。有可能是IP地址,證件號(hào)碼等。
?????*
?????*?
@return
?additional?details?about?the?authentication?request,?or??null?if?not?used
????
*/
????
public
?Object?getDetails();


??
/**?*/
/**
?????*?返回一個(gè)已經(jīng)通過(guò)驗(yàn)證的Principal(這通常是一個(gè)用戶名)。
?????*
?????*?
@return
?the?Principal?being?authenticated
????
*/
????
public
?Object?getPrincipal();


??
/**?*/
/**
???? * AbstractSecurityInterceptor將認(rèn)證令牌交給AuthenticationManager。如果認(rèn)證成功AuthenticaionManager
???? * 會(huì)返回一個(gè)不變的認(rèn)證令牌(返回true)。
???? * 返回"true"會(huì)改善性能,因?yàn)椴辉偈敲恳粋€(gè)請(qǐng)求都需要調(diào)用AuthenticationManager。
???? * 由于安全上的考慮,實(shí)現(xiàn)這個(gè)接口返回true的時(shí)候需要十分的小心。除非他們不再變化或者有什么途徑
???? * 確保屬性在最初的初始化后不再變化。
?????*
?????*?
@return
?true?if?the?token?has?been?authenticated?and?the? AbstractSecurityInterceptor?
?????*????????does?not?need?to?represent?the?token?for?re-authentication?to?the AuthenticationManager
????
*/
????
public
?
boolean
?isAuthenticated();


??
/**?*/
/**
???? * 詳情參閱isAuthenticate()方法
???? * 參數(shù)為true的情況下,如果某個(gè)實(shí)現(xiàn)希望拒絕一個(gè)調(diào)用,那么將拋出一個(gè)IllegalArgumentException異常。
?????*
?????*?
@param
?isAuthenticated?true?if?the?token?should?be?trusted?(which?may?result?in?an?exception)?or
?????*????????false?if?the?token?should?not?be?trusted
?????*
?????*?
@throws
?IllegalArgumentException?if?an?attempt?to?make?the?authentication?token?trusted?(by?
?????*????????passing?true?as?the?argument)?is?rejected?due?to?the?implementation?being?immutable?or
?????*????????implementing?its?own?alternative?approach?to isAuthenticated()}
????
*/
????
public
?
void
?setAuthenticated(
boolean
?isAuthenticated)
????????
throws
?IllegalArgumentException;
}
Acegi提供了一個(gè)能適應(yīng)大多數(shù)情況的ProviderManager,實(shí)現(xiàn)了AuthenticationManager。
ProviderManager繼承抽象類AbstractAuthenticationManager:
public
?
abstract
?
class
?AbstractAuthenticationManager?
implements
?AuthenticationManager?
{
????
//
?~Methods?==================================================================?
?
/**?*/
/**
?????*?實(shí)現(xiàn)通過(guò)調(diào)用抽象方法doAuthentication()進(jìn)行工作。
?????*?如果doAuthentication()方法拋出AuthenticationException異常,驗(yàn)證失敗。
?????*
?????*?
@param
?authRequest?the?authentication?request?object
?????*
?????*?
@return
?a?fully?authenticated?object?including?credentials
?????*
?????*?
@throws
?AuthenticationException?if?authentication?fails
????
*/
????
public
?
final
?Authentication?authenticate(Authentication?authRequest)

????????
throws
?AuthenticationException?
{

????????
try
?
{
????????????Authentication?authResult?
=
?doAuthentication(authRequest);
????????????copyDetails(authRequest,?authResult);

????????????
return
?authResult;

????????}
?
catch
?(AuthenticationException?e)?
{
????????????e.setAuthentication(authRequest);
????????????
throw
?e;
????????}
????}
?
/**?*/
/**
?????*?在目標(biāo)Authentication的detail沒(méi)有被設(shè)置的情況下從源Authentication復(fù)制detail信息。
?????*
?????*?
@param
?source?source?authentication
?????*?
@param
?dest?the?destination?authentication?object
????
*/
????
private
?
void
?copyDetails(Authentication?source,?Authentication?dest)?
{

????????
if
?((dest?
instanceof
?AbstractAuthenticationToken)?
&&
?(dest.getDetails()?
==
?
null
))?
{
????????????AbstractAuthenticationToken?token?
=
?(AbstractAuthenticationToken)?dest;

????????????token.setDetails(source.getDetails());
????????}
????}
?
/**?*/
/**
?????*?具體的實(shí)現(xiàn)通過(guò)覆寫此方法提供認(rèn)證服務(wù),此方法的約束詳見(jiàn)AuthenticationManager的authenticate()方法
?????*
?????*?
@param
?authentication?the?authentication?request?object
?????*
?????*?
@return
?a?fully?authenticated?object?including?credentials
?????*
?????*?
@throws
?AuthenticationException?if?authentication?fails
????
*/
????
protected
?
abstract
?Authentication?doAuthentication(Authentication?authentication)
????????
throws
?AuthenticationException;
}
?
/**?*
/**
??? ?*?認(rèn)證請(qǐng)求重復(fù)的通過(guò)一組AuthenticationProviders容器進(jìn)行認(rèn)證。
??? ?*?可以通過(guò)配置ConcurrentSessionController來(lái)限制單個(gè)用戶的session數(shù)目。
??? ?*?AuthenticationProvider試圖得到一個(gè)非空的響應(yīng)。非空的響應(yīng)表明Provider已經(jīng)對(duì)認(rèn)證請(qǐng)求作出決議
??? ?*?而不需要嘗試其他的Provider。
???? *?如果provider拋出一個(gè)AuthenticationException,這個(gè)異常將會(huì)被保留直到后續(xù)的providers都被嘗試為止。
???? *?如果認(rèn)真請(qǐng)求在后續(xù)的Providers中被認(rèn)證成功,早先的認(rèn)證異常會(huì)被忽略而成功的認(rèn)證信息將被使用。
???? *?如果后續(xù)的Providers沒(méi)有作出一個(gè)非空的響應(yīng)或一個(gè)新的AuthenticationException,那么最后接收到的
???? *?AuthenticationException將會(huì)被使用。
???? *?如果沒(méi)有一個(gè)Provider返回一個(gè)非空的響應(yīng)或是表明能運(yùn)行一個(gè)Authentication,那么ProviderManager將會(huì)
???? *?拋出一個(gè)ProviderNotFoundException異常。
???? *?如果一個(gè)有效的Authentication被AuthenticationProvider返回,ProviderManager將會(huì)發(fā)布一個(gè)org
???? *?.acegisecurity.event.authentication.AuthenticationSuccessEvent。
???? *?如果發(fā)現(xiàn)AuthenticationException,最后的AuthenticationException會(huì)發(fā)布一個(gè)適當(dāng)?shù)氖∈录?br />
???? *?默認(rèn)的ProviderManager將異常和事件對(duì)應(yīng)起來(lái),我們可以通過(guò)定一個(gè)新的exceptionMappings?Properties
???? *?來(lái)調(diào)整對(duì)應(yīng)關(guān)系。
???? *?在properties中,key表現(xiàn)的是異常的類名的完整路徑,value表現(xiàn)的是AbstractAuthenticationFailureEvent的
???? *?子類,提供子類的構(gòu)造。
???? *
??? ?*?
@see
?ConcurrentSessionController
???? */
public
?
class
?ProviderManager?
extends
?AbstractAuthenticationManager?
implements
?InitializingBean,

????ApplicationEventPublisherAware,?MessageSourceAware?
{
????
//
~?Static?fields/initializers?=======================================================?
????
private
?
static
?
final
?Log?logger?
=
?LogFactory.getLog(ProviderManager.
class
);

????
//
~?Instance?fields?=============================================================?
????
private
?ApplicationEventPublisher?applicationEventPublisher;
????
private
?ConcurrentSessionController?sessionController?
=
?
new
?NullConcurrentSessionController();
????
private
?List?providers;
????
protected
?MessageSourceAccessor?messages?
=
?AcegiMessageSource.getAccessor();
????
private
?Properties?exceptionMappings;

????
//
~?Methods?==================================================================?
????
public
?
void
?afterPropertiesSet()?
throws
?Exception?
{
????????checkIfValidList(
this
.providers);
????????Assert.notNull(
this
.messages,?
"
A?message?source?must?be?set
"
);


????????
if
?(exceptionMappings?
==
?
null
)?
{
????????????exceptionMappings?
=
?
new
?Properties();
????????????exceptionMappings.put(AccountExpiredException.
class
.getName(),
????????????????AuthenticationFailureExpiredEvent.
class
.getName());
????????????exceptionMappings.put(AuthenticationServiceException.
class
.getName(),
????????????????AuthenticationFailureServiceExceptionEvent.
class
.getName());
????????????exceptionMappings.put(LockedException.
class
.getName(),?AuthenticationFailureLockedEvent.
class
.getName());
????????????exceptionMappings.put(CredentialsExpiredException.
class
.getName(),
????????????????AuthenticationFailureCredentialsExpiredEvent.
class
.getName());
????????????exceptionMappings.put(DisabledException.
class
.getName(),?AuthenticationFailureDisabledEvent.
class
.getName());
????????????exceptionMappings.put(BadCredentialsException.
class
.getName(),
????????????????AuthenticationFailureBadCredentialsEvent.
class
.getName());
????????????exceptionMappings.put(UsernameNotFoundException.
class
.getName(),
????????????????AuthenticationFailureBadCredentialsEvent.
class
.getName());
????????????exceptionMappings.put(ConcurrentLoginException.
class
.getName(),
????????????????AuthenticationFailureConcurrentLoginEvent.
class
.getName());
????????????exceptionMappings.put(ProviderNotFoundException.
class
.getName(),
????????????????AuthenticationFailureProviderNotFoundEvent.
class
.getName());
????????????exceptionMappings.put(ProxyUntrustedException.
class
.getName(),
????????????????AuthenticationFailureProxyUntrustedEvent.
class
.getName());
????????????doAddExtraDefaultExceptionMappings(exceptionMappings);
????????}
????}
????
private
?
void
?checkIfValidList(List?listToCheck)?
{

????????
if
?((listToCheck?
==
?
null
)?
||
?(listToCheck.size()?
==
?
0
))?
{
????????????
throw
?
new
?IllegalArgumentException(
"
A?list?of?AuthenticationManagers?is?required
"
);
????????}
????}
?
/**?*/
/**
?????*?如果在啟動(dòng)期間沒(méi)有exception被IoC容器注入,這個(gè)方法提供額外的異常對(duì)應(yīng)。
?????*
?????*?
@param
?exceptionMappings?the?properties?object,?which?already?has?entries?in?it
????
*/
????
protected
?
void
?doAddExtraDefaultExceptionMappings(Properties?exceptionMappings)?
{}
?
/**?*/
/**
?????*?嘗試認(rèn)證通過(guò)Authentication對(duì)象。
?????*?AuthenticationProviders組將會(huì)接連嘗試認(rèn)證對(duì)象,直到其中的一個(gè)通過(guò)這個(gè)Authentication對(duì)象。
?????*?如果多個(gè)AuthenticationProvider通過(guò)了Authentication對(duì)象,那么只有第一個(gè)AuthenticationProvider
?????*?產(chǎn)生結(jié)果,后續(xù)的AuthenticationProvider將不會(huì)被嘗試。
?????*
?????*?
@param
?authentication?the?authentication?request?object.
?????*
?????*?
@return
?a?fully?authenticated?object?including?credentials.
?????*
?????*?
@throws
?AuthenticationException?if?authentication?fails.
????
*/
????
public
?Authentication?doAuthentication(Authentication?authentication)

????????
throws
?AuthenticationException?
{
????????Iterator?iter?
=
?providers.iterator();

????????Class?toTest?
=
?authentication.getClass();

????????AuthenticationException?lastException?
=
?
null
;


????????
while
?(iter.hasNext())?
{
????????????AuthenticationProvider?provider?
=
?(AuthenticationProvider)?iter.next();


????????????
if
?(provider.supports(toTest))?
{
????????????????logger.debug(
"
Authentication?attempt?using?
"
?
+
?provider.getClass().getName());

????????????????Authentication?result?
=
?
null
;


????????????????
try
?
{
????????????????????result?
=
?provider.authenticate(authentication);
????????????????????sessionController.checkAuthenticationAllowed(result);

????????????????}
?
catch
?(AuthenticationException?ae)?
{
????????????????????lastException?
=
?ae;
????????????????????result?
=
?
null
;
????????????????}
????????????????
if
?(result?
!=
?
null
)?
{
????????????????????sessionController.registerSuccessfulAuthentication(result);
????????????????????applicationEventPublisher.publishEvent(
new
?AuthenticationSuccessEvent(result));

????????????????????
return
?result;
????????????????}
????????????}
????????}
????????
if
?(lastException?
==
?
null
)?
{
????????????lastException?
=
?
new
?ProviderNotFoundException(messages.getMessage(
"
ProviderManager.providerNotFound
"
,

????????????????????????
new
?Object[]?
{toTest.getName()}
,?
"
No?AuthenticationProvider?found?for?{0}
"
));
????????}
????????
//
?Publish?the?event
????????String?className?
=
?exceptionMappings.getProperty(lastException.getClass().getName());
????????AbstractAuthenticationEvent?event?
=
?
null
;


????????
if
?(className?
!=
?
null
)?
{

????????????
try
?
{
????????????????Class?clazz?
=
?getClass().getClassLoader().loadClass(className);

????????????????Constructor?constructor?
=
?clazz.getConstructor(
new
?Class[]?
{
????????????????????????????Authentication.
class
,?AuthenticationException.
class
????????????????????????}
);

????????????????Object?obj?
=
?constructor.newInstance(
new
?Object[]?
{authentication,?lastException}
);
????????????????Assert.isInstanceOf(AbstractAuthenticationEvent.
class
,?obj,?
"
Must?be?an?AbstractAuthenticationEvent
"
);
????????????????event?
=
?(AbstractAuthenticationEvent)?obj;

????????????}
?
catch
?(ClassNotFoundException?ignored)?
{}
????????????
catch
?(NoSuchMethodException?ignored)?
{}
????????????
catch
?(IllegalAccessException?ignored)?
{}
????????????
catch
?(InstantiationException?ignored)?
{}
????????????
catch
?(InvocationTargetException?ignored)?
{}
????????}
????????
if
?(event?
!=
?
null
)?
{
????????????applicationEventPublisher.publishEvent(event);

????????}
?
else
?
{

????????????
if
?(logger.isDebugEnabled())?
{
????????????????logger.debug(
"
No?event?was?found?for?the?exception?
"
?
+
?lastException.getClass().getName());
????????????}
????????}
????????
//
?Throw?the?exception
????????
throw
?lastException;
????}
????
public
?List?getProviders()?
{
????????
return
?
this
.providers;
????}
?
/**?*/
/**
?????*?返回設(shè)定的ConcurrentSessionController對(duì)象,如果對(duì)象沒(méi)有被設(shè)置則返回一個(gè)
?????*?NullConcurrentSessionController(默認(rèn)初始化的對(duì)象)
?????*
?????*?
@return
?ConcurrentSessionController?instance
????
*/
????
public
?ConcurrentSessionController?getSessionController()?
{
????????
return
?sessionController;
????}
????
public
?
void
?setApplicationEventPublisher(ApplicationEventPublisher?applicationEventPublisher)?
{
????????
this
.applicationEventPublisher?
=
?applicationEventPublisher;
????}
????
public
?
void
?setMessageSource(MessageSource?messageSource)?
{
????????
this
.messages?
=
?
new
?MessageSourceAccessor(messageSource);
????}
?
/**?*/
/**
?????*?設(shè)置AuthenticationProvider對(duì)象
?????*?
?????*?
@param
?newList
?????*
?????*?
@throws
?IllegalArgumentException?DOCUMENT?ME!
????
*/
????
public
?
void
?setProviders(List?newList)?
{
????????checkIfValidList(newList);

????????Iterator?iter?
=
?newList.iterator();


????????
while
?(iter.hasNext())?
{
????????????Object?currentObject?
=
?
null
;


????????????
try
?
{
????????????????currentObject?
=
?iter.next();

????????????????AuthenticationProvider?attemptToCast?
=
?(AuthenticationProvider)?currentObject;

????????????}
?
catch
?(ClassCastException?cce)?
{
????????????????
throw
?
new
?IllegalArgumentException(
"
AuthenticationProvider?
"
?
+
?currentObject.getClass().getName()
????????????????????
+
?
"
?must?implement?AuthenticationProvider
"
);
????????????}
????????}
????????
this
.providers?
=
?newList;
????}
?
/**?*/
/**
?????*?設(shè)置ConcurrentSessionController來(lái)限制用戶的session數(shù)量。
?????*?默認(rèn)設(shè)置為NullConcurrentSessionController
?????*
?????*?
@param
?sessionController?ConcurrentSessionController
????
*/
????
public
?
void
?setSessionController(ConcurrentSessionController?sessionController)?
{
????????
this
.sessionController?
=
?sessionController;
????}
}
AuthenticationManager不依靠自己實(shí)現(xiàn)身份驗(yàn)證,而是通過(guò)Iterator逐個(gè)遍歷AuthenticationProvider的子類集合(如果使用Spring的話子類類型由配置文件注入),直到某個(gè)Provider成功驗(yàn)證Authentication。
Acegi提供的Provider實(shí)現(xiàn)包括:
AuthByAdapterProvider、CasAuthenticationProvider、DaoAuthenticationProvider、JaasAuthenticationProvider、PasswordDaoAuthenticationProvider、RemoteAuthenticationProvider、RunAsImplAuthenticationProvider、TestingAuthenticationProvider。
下面只分析DaoAuthenticationProvider的相關(guān)類,按照AuthenticationProvider-->AbstractUserDetailsAuthenticationProvider-->DaoAuthenticationProvider的順序展開。
/**?*/
/**
???? *?這個(gè)類處理一個(gè)Authenticaon類的具體實(shí)現(xiàn)
???? */
public
?
interface
?AuthenticationProvider?
{
????
//
~?Methods?==================================================================
?
/**?*/
/**
?????*?和AuthenticationManager的同名方法實(shí)現(xiàn)同樣的功能,詳見(jiàn)AuthenticationManager
?????*
?????*?
@param
?authentication?the?authentication?request?object.
?????*
?????*?
@return
?a?fully?authenticated?object?including?credentials.?May?return?null?if?the
?????*?????????AuthenticationProvider?is?unable?to?support?authentication?of?the?passed
?????*?????????Authentication?object.?In?such?a?case,?the?next?AuthenticationProvider?that
?????*?????????supports?the?presented?Authentication?class?will?be?tried.
?????*
?????*?
@throws
?AuthenticationException?if?authentication?fails.
????
*/
????
public
?Authentication?authenticate(Authentication?authentication)
????????
throws
?AuthenticationException;


??
/**?*/
/**
?????*?如果這個(gè)AuthenticationProvider支持通過(guò)Authentication對(duì)象,則返回True。
?????*?返回True并不意味著AuthenticationProvider能認(rèn)證當(dāng)前的Authenctication。
?????*?他只是簡(jiǎn)單的聲明支持通過(guò)認(rèn)證。AuthenticationProvider仍舊能夠通過(guò)authenticate()方法返回null,
?????*?使其它的Provider能夠嘗試認(rèn)證這個(gè)Authentication。(存疑)
?????*?選擇哪一個(gè)AuthenticatonProvider履行鑒定是由ProviderManager在運(yùn)行時(shí)管理的。
?????*
?????*?
@param
?authentication?DOCUMENT?ME!
?????*
?????*?
@return
?true?if?the?implementation?can?more?closely?evaluate?the?Authentication?class
?????*?????????presented
????
*/
????
public
?
boolean
?supports(Class?authentication);
}
/**?*/
/**
???? *?Authentication的基類,允許子類覆寫他的方法以及使用UserDetails對(duì)象
???? *?這個(gè)類被設(shè)計(jì)用來(lái)對(duì)UsernamePasswordAuthenticationToken的認(rèn)證請(qǐng)求作出相應(yīng)。
???? *?在成功驗(yàn)證的基礎(chǔ)上,UsernamePasswordAuthenticationToken會(huì)被構(gòu)造同時(shí)返回給調(diào)用者。
???? *?令牌可以是用戶名的String形式也可以是由認(rèn)證庫(kù)中取得的UserDetails對(duì)象。
???? *?如果適配器容器正在被使用,而又期望得到用戶名的情況下,使用String類型是適當(dāng)?shù)摹?
???? *?如果需要訪問(wèn)額外的用戶信息,例如電子郵件地址、用戶導(dǎo)向的名字等等,那么使用UserDetail是恰
???? *?當(dāng)?shù)摹serDetail提供了更靈活的訪問(wèn),所以通常情況下我們返回一個(gè)UserDetail而并不推薦使用適配
???? *?器容器。如果想要覆寫默認(rèn)的方式,將setForcePrincipalAsString置true即可。
???? *?UserDetails通過(guò)緩存放置在UserCache中。這確保了如果后續(xù)的請(qǐng)求在用戶名相同的情況下可以不通過(guò)
???? *?UserDetailsService查詢。需要指出的是:如果用戶無(wú)意間輸入了錯(cuò)誤的密碼,UserDetailService去查詢
??? ?*?出用戶最后一次輸入的密碼并與之比較。
???? */
public
?
abstract
?
class
?AbstractUserDetailsAuthenticationProvider?
implements
?AuthenticationProvider,?InitializingBean,

????MessageSourceAware?
{
????
//
~?Instance?fields?=============================================================
????
protected
?MessageSourceAccessor?messages?
=
?AcegiMessageSource.getAccessor();
????
private
?UserCache?userCache?
=
?
new
?NullUserCache();
????
private
?
boolean
?forcePrincipalAsString?
=
?
false
;
????
protected
?
boolean
?hideUserNotFoundExceptions?
=
?
true
;

????
//
~?Methods?==================================================================
?
/**?*/
/**
?????*?允許子類對(duì)認(rèn)證請(qǐng)求返回或緩存的UserDetails提供額外的校驗(yàn)。
?????*?通常情況下子類至少會(huì)對(duì)Authentication的getCredentials()方法和UserDetails的getPassword()方法進(jìn)行
?????*?比照。
?????*?如果定制的邏輯需要比照額外的UserDetail屬性或者UsernamePasswordAuthenticationToken,也應(yīng)該在
?????*?這個(gè)方法中定制。
?????*
?????*?
@param
?userDetails?as?retrieved?from?the??#retrieveUser(String,?
?????*????????UsernamePasswordAuthenticationToken)}?or UserCache
?????*?
@param
?authentication?the?current?request?that?needs?to?be?authenticated
?????*
?????*?
@throws
?AuthenticationException?AuthenticationException?if?the?credentials?could?not?be?validated?
?????*????????(generally?a ?BadCredentialsException?an?AuthenticationServiceException)
????
*/
????
protected
?
abstract
?
void
?additionalAuthenticationChecks(UserDetails?userDetails,
????????UsernamePasswordAuthenticationToken?authentication)
????????
throws
?AuthenticationException;


????
public
?
final
?
void
?afterPropertiesSet()?
throws
?Exception?
{
????????Assert.notNull(
this
.userCache,?
"
A?user?cache?must?be?set
"
);
????????Assert.notNull(
this
.messages,?
"
A?message?source?must?be?set
"
);
????????doAfterPropertiesSet();
????}
????
public
?Authentication?authenticate(Authentication?authentication)

????????
throws
?AuthenticationException?
{
????????Assert.isInstanceOf(UsernamePasswordAuthenticationToken.
class
,?authentication,
????????????messages.getMessage(
"
AbstractUserDetailsAuthenticationProvider.onlySupports
"
,
????????????????
"
Only?UsernamePasswordAuthenticationToken?is?supported
"
));

????????
//
?Determine?username
????????String?username?
=
?(authentication.getPrincipal()?
==
?
null
)?
?
?
"
NONE_PROVIDED
"
?:?authentication.getName();

????????
boolean
?cacheWasUsed?
=
?
true
;
????????UserDetails?user?
=
?
this
.userCache.getUserFromCache(username);


????????
if
?(user?
==
?
null
)?
{
????????????cacheWasUsed?
=
?
false
;


????????????
try
?
{
????????????????user?
=
?retrieveUser(username,?(UsernamePasswordAuthenticationToken)?authentication);

????????????}
?
catch
?(UsernameNotFoundException?notFound)?
{

????????????????
if
?(hideUserNotFoundExceptions)?
{
????????????????????
throw
?
new
?BadCredentialsException(messages.getMessage(
????????????????????????????
"
AbstractUserDetailsAuthenticationProvider.badCredentials
"
,?
"
Bad?credentials
"
));

????????????????}
?
else
?
{
????????????????????
throw
?notFound;
????????????????}
????????????}
????????????Assert.notNull(user,?
"
retrieveUser?returned?null?-?a?violation?of?the?interface?contract
"
);
????????}
????????
if
?(
!
user.isAccountNonLocked())?
{
????????????
throw
?
new
?LockedException(messages.getMessage(
"
AbstractUserDetailsAuthenticationProvider.locked
"
,
????????????????????
"
User?account?is?locked
"
));
????????}
????????
if
?(
!
user.isEnabled())?
{
????????????
throw
?
new
?DisabledException(messages.getMessage(
"
AbstractUserDetailsAuthenticationProvider.disabled
"
,
????????????????????
"
User?is?disabled
"
));
????????}
????????
if
?(
!
user.isAccountNonExpired())?
{
????????????
throw
?
new
?AccountExpiredException(messages.getMessage(
"
AbstractUserDetailsAuthenticationProvider.expired
"
,
????????????????????
"
User?account?has?expired
"
));
????????}
????????
//
?This?check?must?come?here,?as?we?don't?want?to?tell?users
????????
//
?about?account?status?unless?they?presented?the?correct?credentials
????????
try
?
{
????????????additionalAuthenticationChecks(user,?(UsernamePasswordAuthenticationToken)?authentication);

????????}
?
catch
?(AuthenticationException?exception)?
{
????????????
//
?There?was?a?problem,?so?try?again?after?checking?we're?using?latest?data
????????????cacheWasUsed?
=
?
false
;
????????????user?
=
?retrieveUser(username,?(UsernamePasswordAuthenticationToken)?authentication);
????????????additionalAuthenticationChecks(user,?(UsernamePasswordAuthenticationToken)?authentication);
????????}
????????
if
?(
!
user.isCredentialsNonExpired())?
{
????????????
throw
?
new
?CredentialsExpiredException(messages.getMessage(
????????????????????
"
AbstractUserDetailsAuthenticationProvider.credentialsExpired
"
,?
"
User?credentials?have?expired
"
));
????????}
????????
if
?(
!
cacheWasUsed)?
{
????????????
this
.userCache.putUserInCache(user);
????????}
????????Object?principalToReturn?
=
?user;


????????
if
?(forcePrincipalAsString)?
{
????????????principalToReturn?
=
?user.getUsername();
????????}
????????
return
?createSuccessAuthentication(principalToReturn,?authentication,?user);
????}
?
/**?*/
/**
?????*?構(gòu)建一個(gè)成功的Authentication對(duì)象。使用的保護(hù)類型所以子類可以覆寫本方法。
?????*?子類往往會(huì)將用戶提供的原始信任狀(未經(jīng)修改以及密碼未被解密)儲(chǔ)存在返回的Authentication
?????*?對(duì)象中。
?????*
?????*?
@param
?principal?that?should?be?the?principal?in?the?returned?object?(defined?by?the?
?????*????????#isForcePrincipalAsString()?method)
?????*?
@param
?authentication?that?was?presented?to?the?provider?for?validation
?????*?
@param
?user?that?was?loaded?by?the?implementation
?????*
?????*?
@return
?the?successful?authentication?token
????
*/
????
protected
?Authentication?createSuccessAuthentication(Object?principal,?Authentication?authentication,

????????UserDetails?user)?
{
????????
//
?Ensure?we?return?the?original?credentials?the?user?supplied,
????????
//
?so?subsequent?attempts?are?successful?even?with?encoded?passwords.
????????
//
?Also?ensure?we?return?the?original?getDetails(),?so?that?future
????????
//
?authentication?events?after?cache?expiry?contain?the?details
????????UsernamePasswordAuthenticationToken?result?
=
?
new
?UsernamePasswordAuthenticationToken(principal,
????????????????authentication.getCredentials(),?user.getAuthorities());
????????result.setDetails(authentication.getDetails());

????????
return
?result;
????}
????
protected
?
void
?doAfterPropertiesSet()?
throws
?Exception?
{}
????
public
?UserCache?getUserCache()?
{
????????
return
?userCache;
????}
????
public
?
boolean
?isForcePrincipalAsString()?
{
????????
return
?forcePrincipalAsString;
????}
????
public
?
boolean
?isHideUserNotFoundExceptions()?
{
????????
return
?hideUserNotFoundExceptions;
????}
?
/**?*/
/**
?????*?允許子類由定義的實(shí)現(xiàn)位置獲取UserDetails,如果呈上的信任狀是非法的可以有選擇的拋出
?????*?AuthenticationExcepton異常(在有必要將用戶與資源綁定來(lái)獲取或產(chǎn)生UserDetail的情況下,這種處理
?????*?方式是十分有效的)。
?????*?子類沒(méi)有必要去實(shí)現(xiàn)任何的緩存,因?yàn)锳bstractUserDetailsAuthenticationProvider在默認(rèn)的情況下
?????*?將會(huì)緩存UserDetails。
?????*?UserDetails的緩存是十分復(fù)雜的,這意味著后續(xù)的認(rèn)證請(qǐng)求即使是從緩存中得到回復(fù)也仍舊要進(jìn)行
?????*?信任狀的校驗(yàn),即使信任狀的校驗(yàn)被基類在這個(gè)方法中用binding-based策略實(shí)現(xiàn)。
?????*?因此在子類禁用緩存(如果想要保證這個(gè)方法是唯一能夠進(jìn)行認(rèn)證的方法,沒(méi)有UserDetails將會(huì)
?????*?被緩存)或是確認(rèn)子類實(shí)現(xiàn)additionalAuthenticationChecks()方法來(lái)進(jìn)行被緩存的UserDetails
?????*?對(duì)象的信任狀和后續(xù)的認(rèn)證請(qǐng)求的比照的情況下它是十分重要的。
?????*?在大多數(shù)情況下子類不必要在這個(gè)方法中實(shí)現(xiàn)信任狀的校驗(yàn),取而代之的是在
?????*?additionalAuthenticationChecks()方法中實(shí)現(xiàn)。這樣代碼不用在2個(gè)方法中都實(shí)現(xiàn)信任狀的校驗(yàn)。
?????*
?????*?
@param
?username?The?username?to?retrieve
?????*?
@param
?authentication?The?authentication?request,?which?subclasses?may?need?to?perform?a?binding-based
?????*????????retrieval?of?the?UserDetails
?????*
?????*?
@return
?the?user?information?(never?null?-?instead?an?exception?should?the?thrown)
?????*
?????*?
@throws
?AuthenticationException?if?the?credentials?could?not?be?validated?(generally?a
?????*?????????BadCredentialsExceptionan?,an?AuthenticationServiceException?or
?????*?????????UsernameNotFoundException)
????
*/
????
protected
?
abstract
?UserDetails?retrieveUser(String?username,?UsernamePasswordAuthenticationToken?authentication)
????????
throws
?AuthenticationException;


????
public
?
void
?setForcePrincipalAsString(
boolean
?forcePrincipalAsString)?
{
????????
this
.forcePrincipalAsString?
=
?forcePrincipalAsString;
????}
?
/**?*/
/**
?????*?在通常情況,如果用戶名沒(méi)有找到或是密碼錯(cuò)誤AbstractUserDetailsAuthenticationProvider將會(huì)
?????*?拋出一個(gè)BadCredentialsException異常。設(shè)置這個(gè)屬性為false的話,程序?qū)?huì)拋出
?????*?UsernameNotFoundException來(lái)取代BadCredentialsException異常。
?????*?需要注意的是:我們認(rèn)為這不如拋出BadCredentialsException來(lái)的可靠。
?????*
?????*?
@param
?hideUserNotFoundExceptions?set?to?false?if?you?wish?UsernameNotFoundExceptions
?????*????????to?be?thrown?instead?of?the?non-specific?BadCredentialsException?(defaults?to
?????*????????true)
????
*/
????
public
?
void
?setHideUserNotFoundExceptions(
boolean
?hideUserNotFoundExceptions)?
{
????????
this
.hideUserNotFoundExceptions?
=
?hideUserNotFoundExceptions;
????}
????
public
?
void
?setMessageSource(MessageSource?messageSource)?
{
????????
this
.messages?
=
?
new
?MessageSourceAccessor(messageSource);
????}
????
public
?
void
?setUserCache(UserCache?userCache)?
{
????????
this
.userCache?
=
?userCache;
????}
????
public
?
boolean
?supports(Class?authentication)?
{
????????
return
?(UsernamePasswordAuthenticationToken.
class
.isAssignableFrom(authentication));
????}
}
?
/**?*/
/**
???? *?AuthenticationProvider的具體實(shí)現(xiàn),由UserDetailsService獲取用戶的詳細(xì)信息。
???? *
???? */
public
?
class
?DaoAuthenticationProvider?
extends
?AbstractUserDetailsAuthenticationProvider?
{
????
//
~?Instance?fields?=============================================================
????
private
?PasswordEncoder?passwordEncoder?
=
?
new
?PlaintextPasswordEncoder();
????
private
?SaltSource?saltSource;
????
private
?UserDetailsService?userDetailsService;

????
//
~?Methods?==================================================================
????
protected
?
void
?additionalAuthenticationChecks(UserDetails?userDetails,
????????UsernamePasswordAuthenticationToken?authentication)

????????
throws
?AuthenticationException?
{
????????Object?salt?
=
?
null
;


????????
if
?(
this
.saltSource?
!=
?
null
)?
{
????????????salt?
=
?
this
.saltSource.getSalt(userDetails);
????????}
????????
if
?(
!
passwordEncoder.isPasswordValid(userDetails.getPassword(),?authentication.getCredentials().toString(),?salt))?
{
????????????
throw
?
new
?BadCredentialsException(messages.getMessage(
????????????????????
"
AbstractUserDetailsAuthenticationProvider.badCredentials
"
,?
"
Bad?credentials
"
),?userDetails);
????????}
????}
????
protected
?
void
?doAfterPropertiesSet()?
throws
?Exception?
{
????????Assert.notNull(
this
.userDetailsService,?
"
An?Authentication?DAO?must?be?set
"
);
????}
????
public
?PasswordEncoder?getPasswordEncoder()?
{
????????
return
?passwordEncoder;
????}
????
public
?SaltSource?getSaltSource()?
{
????????
return
?saltSource;
????}
????
public
?UserDetailsService?getUserDetailsService()?
{
????????
return
?userDetailsService;
????}
????
protected
?
final
?UserDetails?retrieveUser(String?username,?UsernamePasswordAuthenticationToken?authentication)

????????
throws
?AuthenticationException?
{
????????UserDetails?loadedUser;


????????
try
?
{
????????????loadedUser?
=
?
this
.getUserDetailsService().loadUserByUsername(username);

????????}
?
catch
?(DataAccessException?repositoryProblem)?
{
????????????
throw
?
new
?AuthenticationServiceException(repositoryProblem.getMessage(),?repositoryProblem);
????????}
????????
if
?(loadedUser?
==
?
null
)?
{
????????????
throw
?
new
?AuthenticationServiceException(
????????????????
"
AuthenticationDao?returned?null,?which?is?an?interface?contract?violation
"
);
????????}
????????
return
?loadedUser;
????}
?
/**?*/
/**
?????*?設(shè)置密碼解密實(shí)例來(lái)進(jìn)行密碼的解密校驗(yàn)。如果沒(méi)有設(shè)置,將會(huì)使用PlaintextPosswordEncoder
?????*?作為默認(rèn)設(shè)置。
?????*
?????*?
@param
?passwordEncoder?The?passwordEncoder?to?use
????
*/
????
public
?
void
?setPasswordEncoder(PasswordEncoder?passwordEncoder)?
{
????????
this
.passwordEncoder?
=
?passwordEncoder;
????}
?
/**?*/
/**
?????*?(source?of?salts翻譯不來(lái))此方法解迷密碼的時(shí)候使用。
?????*?null是一個(gè)合法得值,這表明DaoAuthenticationProvider會(huì)把null返回給PasswordEncoder。
?????*
?????*?
@param
?saltSource?to?use?when?attempting?to?decode?passwords?via?the?PasswordEncoder
????
*/
????
public
?
void
?setSaltSource(SaltSource?saltSource)?
{
????????
this
.saltSource?
=
?saltSource;
????}
????
public
?
void
?setUserDetailsService(UserDetailsService?authenticationDao)?
{
????????
this
.userDetailsService?
=
?authenticationDao;
????}
}
?