在Acegi中是由認證管理器確定用戶身份。一個認證管理器由借口AuthenticationManager實現。
public
?
interface
?AuthenticationManager?
{
????
//
~Methods?==================================================================?
???

???
/**?*
/**
?????*?嘗試驗證用戶身份,如果驗證成功,將返回一個含授權的完整的Authentication對象。
?????*?一個AuthenticationManager必須按照下面的規則處理異常:
?????*?如果賬號是不被允許的必須拋出DisabledException,AuthenticationManager能檢測到這個狀態。
?????*?如果賬號已經被鎖定必須拋出LockedException,AuthenticationManager能夠檢測被鎖定的賬號。
?????*?如果取到的是非法的信任狀(credential?)必須拋出BadCredentialsException。同時上述的2種異常也是可選
?????*?的,AuthenticationManager必須總是檢測信任狀(credential)。
?????*?異常必須被檢測,如果有上述的異常出現須拋出異常。
?????*?如果一個賬號是不被允許的或是被鎖定的,認證請求會立即被拒絕,信任狀的檢測不會發生。
?????*?這防止了對不被允許賬號和被鎖定的賬號的信任狀檢測。
?????*
?????*?
@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實現了簡單的主體(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?==================================================================
?
/**?*/
/**
?????*?通過AuthenticationManager設置Principal的授權信息。
?????*?注意:當Class狀態為valid時,除非Value已經被可信任的AuthenticationManager設置t過,否則對Class
?????*?不起作用。(此句存疑?Note?that?classes?should?not?rely?on?this?value?as?being?valid?unless?it?has?
?????*?been?set?by?AuthenticationManager)
?????*?Authentication的實現類需要確保修改返回的數足不會影響Authentication?Object的狀態。(比如返回一個
?????*?數組的copy)。
?????*?
?????*?
@return
?the?authorities?granted?to?the?principal,?or?null?if?authentication?has?not?been?completed
????
*/
????
public
?GrantedAuthority[]?getAuthorities();


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


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


??
/**?*/
/**
?????*?返回一個已經通過驗證的Principal(這通常是一個用戶名)。
?????*
?????*?
@return
?the?Principal?being?authenticated
????
*/
????
public
?Object?getPrincipal();


??
/**?*/
/**
???? * AbstractSecurityInterceptor將認證令牌交給AuthenticationManager。如果認證成功AuthenticaionManager
???? * 會返回一個不變的認證令牌(返回true)。
???? * 返回"true"會改善性能,因為不再是每一個請求都需要調用AuthenticationManager。
???? * 由于安全上的考慮,實現這個接口返回true的時候需要十分的小心。除非他們不再變化或者有什么途徑
???? * 確保屬性在最初的初始化后不再變化。
?????*
?????*?
@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()方法
???? * 參數為true的情況下,如果某個實現希望拒絕一個調用,那么將拋出一個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提供了一個能適應大多數情況的ProviderManager,實現了AuthenticationManager。
ProviderManager繼承抽象類AbstractAuthenticationManager:
public
?
abstract
?
class
?AbstractAuthenticationManager?
implements
?AuthenticationManager?
{
????
//
?~Methods?==================================================================?
?
/**?*/
/**
?????*?實現通過調用抽象方法doAuthentication()進行工作。
?????*?如果doAuthentication()方法拋出AuthenticationException異常,驗證失敗。
?????*
?????*?
@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;
????????}
????}
?
/**?*/
/**
?????*?在目標Authentication的detail沒有被設置的情況下從源Authentication復制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());
????????}
????}
?
/**?*/
/**
?????*?具體的實現通過覆寫此方法提供認證服務,此方法的約束詳見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;
}
?
/**?*
/**
??? ?*?認證請求重復的通過一組AuthenticationProviders容器進行認證。
??? ?*?可以通過配置ConcurrentSessionController來限制單個用戶的session數目。
??? ?*?AuthenticationProvider試圖得到一個非空的響應。非空的響應表明Provider已經對認證請求作出決議
??? ?*?而不需要嘗試其他的Provider。
???? *?如果provider拋出一個AuthenticationException,這個異常將會被保留直到后續的providers都被嘗試為止。
???? *?如果認真請求在后續的Providers中被認證成功,早先的認證異常會被忽略而成功的認證信息將被使用。
???? *?如果后續的Providers沒有作出一個非空的響應或一個新的AuthenticationException,那么最后接收到的
???? *?AuthenticationException將會被使用。
???? *?如果沒有一個Provider返回一個非空的響應或是表明能運行一個Authentication,那么ProviderManager將會
???? *?拋出一個ProviderNotFoundException異常。
???? *?如果一個有效的Authentication被AuthenticationProvider返回,ProviderManager將會發布一個org
???? *?.acegisecurity.event.authentication.AuthenticationSuccessEvent。
???? *?如果發現AuthenticationException,最后的AuthenticationException會發布一個適當的失敗事件。
???? *?默認的ProviderManager將異常和事件對應起來,我們可以通過定一個新的exceptionMappings?Properties
???? *?來調整對應關系。
???? *?在properties中,key表現的是異常的類名的完整路徑,value表現的是AbstractAuthenticationFailureEvent的
???? *?子類,提供子類的構造。
???? *
??? ?*?
@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
"
);
????????}
????}
?
/**?*/
/**
?????*?如果在啟動期間沒有exception被IoC容器注入,這個方法提供額外的異常對應。
?????*
?????*?
@param
?exceptionMappings?the?properties?object,?which?already?has?entries?in?it
????
*/
????
protected
?
void
?doAddExtraDefaultExceptionMappings(Properties?exceptionMappings)?
{}
?
/**?*/
/**
?????*?嘗試認證通過Authentication對象。
?????*?AuthenticationProviders組將會接連嘗試認證對象,直到其中的一個通過這個Authentication對象。
?????*?如果多個AuthenticationProvider通過了Authentication對象,那么只有第一個AuthenticationProvider
?????*?產生結果,后續的AuthenticationProvider將不會被嘗試。
?????*
?????*?
@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;
????}
?
/**?*/
/**
?????*?返回設定的ConcurrentSessionController對象,如果對象沒有被設置則返回一個
?????*?NullConcurrentSessionController(默認初始化的對象)
?????*
?????*?
@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);
????}
?
/**?*/
/**
?????*?設置AuthenticationProvider對象
?????*?
?????*?
@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;
????}
?
/**?*/
/**
?????*?設置ConcurrentSessionController來限制用戶的session數量。
?????*?默認設置為NullConcurrentSessionController
?????*
?????*?
@param
?sessionController?ConcurrentSessionController
????
*/
????
public
?
void
?setSessionController(ConcurrentSessionController?sessionController)?
{
????????
this
.sessionController?
=
?sessionController;
????}
}
AuthenticationManager不依靠自己實現身份驗證,而是通過Iterator逐個遍歷AuthenticationProvider的子類集合(如果使用Spring的話子類類型由配置文件注入),直到某個Provider成功驗證Authentication。
Acegi提供的Provider實現包括:
AuthByAdapterProvider、CasAuthenticationProvider、DaoAuthenticationProvider、JaasAuthenticationProvider、PasswordDaoAuthenticationProvider、RemoteAuthenticationProvider、RunAsImplAuthenticationProvider、TestingAuthenticationProvider。
下面只分析DaoAuthenticationProvider的相關類,按照AuthenticationProvider-->AbstractUserDetailsAuthenticationProvider-->DaoAuthenticationProvider的順序展開。
/**?*/
/**
???? *?這個類處理一個Authenticaon類的具體實現
???? */
public
?
interface
?AuthenticationProvider?
{
????
//
~?Methods?==================================================================
?
/**?*/
/**
?????*?和AuthenticationManager的同名方法實現同樣的功能,詳見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;


??
/**?*/
/**
?????*?如果這個AuthenticationProvider支持通過Authentication對象,則返回True。
?????*?返回True并不意味著AuthenticationProvider能認證當前的Authenctication。
?????*?他只是簡單的聲明支持通過認證。AuthenticationProvider仍舊能夠通過authenticate()方法返回null,
?????*?使其它的Provider能夠嘗試認證這個Authentication。(存疑)
?????*?選擇哪一個AuthenticatonProvider履行鑒定是由ProviderManager在運行時管理的。
?????*
?????*?
@param
?authentication?DOCUMENT?ME!
?????*
?????*?
@return
?true?if?the?implementation?can?more?closely?evaluate?the?Authentication?class
?????*?????????presented
????
*/
????
public
?
boolean
?supports(Class?authentication);
}
/**?*/
/**
???? *?Authentication的基類,允許子類覆寫他的方法以及使用UserDetails對象
???? *?這個類被設計用來對UsernamePasswordAuthenticationToken的認證請求作出相應。
???? *?在成功驗證的基礎上,UsernamePasswordAuthenticationToken會被構造同時返回給調用者。
???? *?令牌可以是用戶名的String形式也可以是由認證庫中取得的UserDetails對象。
???? *?如果適配器容器正在被使用,而又期望得到用戶名的情況下,使用String類型是適當的。?
???? *?如果需要訪問額外的用戶信息,例如電子郵件地址、用戶導向的名字等等,那么使用UserDetail是恰
???? *?當的。UserDetail提供了更靈活的訪問,所以通常情況下我們返回一個UserDetail而并不推薦使用適配
???? *?器容器。如果想要覆寫默認的方式,將setForcePrincipalAsString置true即可。
???? *?UserDetails通過緩存放置在UserCache中。這確保了如果后續的請求在用戶名相同的情況下可以不通過
???? *?UserDetailsService查詢。需要指出的是:如果用戶無意間輸入了錯誤的密碼,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?==================================================================
?
/**?*/
/**
?????*?允許子類對認證請求返回或緩存的UserDetails提供額外的校驗。
?????*?通常情況下子類至少會對Authentication的getCredentials()方法和UserDetails的getPassword()方法進行
?????*?比照。
?????*?如果定制的邏輯需要比照額外的UserDetail屬性或者UsernamePasswordAuthenticationToken,也應該在
?????*?這個方法中定制。
?????*
?????*?
@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);
????}
?
/**?*/
/**
?????*?構建一個成功的Authentication對象。使用的保護類型所以子類可以覆寫本方法。
?????*?子類往往會將用戶提供的原始信任狀(未經修改以及密碼未被解密)儲存在返回的Authentication
?????*?對象中。
?????*
?????*?
@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;
????}
?
/**?*/
/**
?????*?允許子類由定義的實現位置獲取UserDetails,如果呈上的信任狀是非法的可以有選擇的拋出
?????*?AuthenticationExcepton異常(在有必要將用戶與資源綁定來獲取或產生UserDetail的情況下,這種處理
?????*?方式是十分有效的)。
?????*?子類沒有必要去實現任何的緩存,因為AbstractUserDetailsAuthenticationProvider在默認的情況下
?????*?將會緩存UserDetails。
?????*?UserDetails的緩存是十分復雜的,這意味著后續的認證請求即使是從緩存中得到回復也仍舊要進行
?????*?信任狀的校驗,即使信任狀的校驗被基類在這個方法中用binding-based策略實現。
?????*?因此在子類禁用緩存(如果想要保證這個方法是唯一能夠進行認證的方法,沒有UserDetails將會
?????*?被緩存)或是確認子類實現additionalAuthenticationChecks()方法來進行被緩存的UserDetails
?????*?對象的信任狀和后續的認證請求的比照的情況下它是十分重要的。
?????*?在大多數情況下子類不必要在這個方法中實現信任狀的校驗,取而代之的是在
?????*?additionalAuthenticationChecks()方法中實現。這樣代碼不用在2個方法中都實現信任狀的校驗。
?????*
?????*?
@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;
????}
?
/**?*/
/**
?????*?在通常情況,如果用戶名沒有找到或是密碼錯誤AbstractUserDetailsAuthenticationProvider將會
?????*?拋出一個BadCredentialsException異常。設置這個屬性為false的話,程序將會拋出
?????*?UsernameNotFoundException來取代BadCredentialsException異常。
?????*?需要注意的是:我們認為這不如拋出BadCredentialsException來的可靠。
?????*
?????*?
@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的具體實現,由UserDetailsService獲取用戶的詳細信息。
???? *
???? */
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;
????}
?
/**?*/
/**
?????*?設置密碼解密實例來進行密碼的解密校驗。如果沒有設置,將會使用PlaintextPosswordEncoder
?????*?作為默認設置。
?????*
?????*?
@param
?passwordEncoder?The?passwordEncoder?to?use
????
*/
????
public
?
void
?setPasswordEncoder(PasswordEncoder?passwordEncoder)?
{
????????
this
.passwordEncoder?
=
?passwordEncoder;
????}
?
/**?*/
/**
?????*?(source?of?salts翻譯不來)此方法解迷密碼的時候使用。
?????*?null是一個合法得值,這表明DaoAuthenticationProvider會把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;
????}
}
?