驗(yàn)證身份的對(duì)象元素
在shiro中,用戶需要提供principals (身份)和credentials(證明)給shiro,從而應(yīng)用能驗(yàn)證用戶身份:
principals:身份,即主體的標(biāo)識(shí)屬性,可以是任何東西,如用戶名、郵箱等,唯一即可。一個(gè)主體可以有多個(gè)principals,但只有一個(gè)Primary principals,一般是用戶名/密碼/手機(jī)號(hào)。
credentials:證明/憑證,即只有主體知道的安全值,如密碼/數(shù)字證書等。
認(rèn)證流程
securiyManager是驗(yàn)證開始的地方,但從數(shù)據(jù)源取數(shù)據(jù)并作比較的工作是由Realm來進(jìn)行的
ModularRealmAuthenticator:
protected AuthenticationInfo doMultiRealmAuthentication(Collection<Realm> realms, AuthenticationToken token) {
//獲取認(rèn)證策略
AuthenticationStrategy strategy = getAuthenticationStrategy();
AuthenticationInfo aggregate = strategy.beforeAllAttempts(realms, token);
if (log.isTraceEnabled()) {
log.trace("Iterating through {} realms for PAM authentication", realms.size());
}
//循環(huán)realm
for (Realm realm : realms) {
aggregate = strategy.beforeAttempt(realm, token, aggregate);
if (realm.supports(token)) {
log.trace("Attempting to authenticate token [{}] using realm [{}]", token, realm);
AuthenticationInfo info = null;
Throwable t = null;
try {
//關(guān)鍵:調(diào)用realm的getAuthenticationInfo進(jìn)行認(rèn)證,token為用戶的token
info = realm.getAuthenticationInfo(token);
} catch (Throwable throwable) {
t = throwable;
if (log.isDebugEnabled()) {
String msg = "Realm [" + realm + "] threw an exception during a multi-realm authentication attempt:";
log.debug(msg, t);
}
}
aggregate = strategy.afterAttempt(realm, token, info, aggregate, t);
} else {
log.debug("Realm [{}] does not support token {}. Skipping realm.", realm, token);
}
}
aggregate = strategy.afterAllAttempts(token, aggregate);
return aggregate;
}
?在Realm開始處理驗(yàn)證的邏輯之前,Authenticator將調(diào)用Realm的 supports 方法去驗(yàn)證當(dāng)前Realm是否支持獲得的AuthenticationToken。
boolean supports (AuthenticationToken token);
通常,Realm檢查的是token的類型,比如在 AuthenticatingRealm 中檢查類型是否相同。
public boolean supports(AuthenticationToken token) {
return token != null && getAuthenticationTokenClass().isAssignableFrom(token.getClass());
}
?
另外,AuthenticatingRealm的constructor中類型默認(rèn)為
authenticationTokenClass = UsernamePasswordToken .class;
如果當(dāng)前Realm支持提交過來的token,authenticator則調(diào)用 getAuthenticationInfo (token) 方法。
?以 AuthenticatingRealm 為例(注意是final):
public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//先從緩存中取
//token為需驗(yàn)證的用戶信息(前臺(tái)傳來的需要驗(yàn)證的用戶)
//info為根據(jù)需驗(yàn)證的用戶信息(前臺(tái)傳來的需要驗(yàn)證的用戶)獲得校驗(yàn)的用戶驗(yàn)證信息(數(shù)據(jù)源中獲得的正確的用戶信息)
AuthenticationInfo info = getCachedAuthenticationInfo(token);
if (info == null) {
//otherwise not cached, perform the lookup:
//自定義Realm的擴(kuò)展點(diǎn)
//根據(jù)需要驗(yàn)證的用戶信息獲得正確的用戶信息(獲得方式:數(shù)據(jù)庫,配置文件等)
info = doGetAuthenticationInfo(token);
log.debug("Looked up AuthenticationInfo [{}] from doGetAuthenticationInfo", info);
if (token != null && info != null) {
cacheAuthenticationInfoIfPossible(token, info);
}
} else {
log.debug("Using cached authentication info [{}] to perform credentials matching.", info);
}
if (info != null) {
//真正的驗(yàn)證,token,info
//此驗(yàn)證內(nèi),如果驗(yàn)證不對(duì),則拋出異常
assertCredentialsMatch(token, info);
} else {
log.debug("No AuthenticationInfo found for submitted AuthenticationToken [{}]. Returning null.", token);
}
return info;
}
?有些人直接在doGetAuthenticationInfo該方法中完成也驗(yàn)證,驗(yàn)證通過時(shí)返回SimpleAuthenticationInfo實(shí)例,失敗則拋出相應(yīng)的驗(yàn)證異常。
但下面有個(gè) assertCredentialsMatch ,說明doGetAuthenticationInfo本沒有打算這樣用,這種使用方式會(huì)讓 CredentialMatcher 失去意義。
protected void assertCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) throws AuthenticationException {
//獲得CredentialsMatcher,有多種實(shí)現(xiàn)
CredentialsMatcher cm = getCredentialsMatcher();
if (cm != null) {
//擴(kuò)展點(diǎn)
//認(rèn)證失敗拋出異常
if (!cm.doCredentialsMatch(token, info)) {
//not successful - throw an exception to indicate this:
String msg = "Submitted credentials for token [" + token + "] did not match the expected credentials.";
throw new IncorrectCredentialsException(msg);
}
} else {
throw new AuthenticationException("A CredentialsMatcher must be configured in order to verify " +
"credentials during authentication. If you do not wish for credentials to be examined, you " +
"can configure an " + AllowAllCredentialsMatcher.class.getName() + " instance.");
}
}
?AuthenticatingRealm的默認(rèn)CredentialMatcher是SimpleCredentialsMatcher
public AuthenticatingRealm() {
this(null, new SimpleCredentialsMatcher());
}
?
protected boolean equals(Object tokenCredentials, Object accountCredentials) {
if (log.isDebugEnabled()) {
log.debug("Performing credentials equality check for tokenCredentials of type [" +
tokenCredentials.getClass().getName() + " and accountCredentials of type [" +
accountCredentials.getClass().getName() + "]");
}
if (isByteSource(tokenCredentials) && isByteSource(accountCredentials)) {
if (log.isDebugEnabled()) {
log.debug("Both credentials arguments can be easily converted to byte arrays. Performing " +
"array equals comparison");
}
byte[] tokenBytes = toBytes(tokenCredentials);
byte[] accountBytes = toBytes(accountCredentials);
return Arrays.equals(tokenBytes, accountBytes);
} else {
return accountCredentials.equals(tokenCredentials);
}
}
?當(dāng)然,實(shí)現(xiàn)類還有HashedCredentialsMatcher,使用密碼加密的方式提高安全性
<!--EndFragment-->
?
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061
微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元

