上一次總結了如何實作一個簡單的Shiro認證流程。首先通過前端頁面的form表單送出,在Controller
請求處理層擷取了form表單中的賬号密碼,然後擷取目前使用者的Subject對象,執行了Subject的login方法進行登入操作,
并将賬号密碼封裝進Token對象,作為參數傳入。而後面設定了認證需要的Realm類,該Realm類繼承了AuthenticatingRealm父類,
實作了doGetAuthenticationInfo方法,在doGetAuthenticationInfo方法中擷取使用者的賬号密碼,在做完一些校驗後,
傳遞給了SimpleAuthenticationInfo,并傳回出去:
package com.test.shiro.realms;
import java.util.HashMap;
import java.util.Map;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.realm.AuthenticatingRealm;
import com.test.shiro.po.User;
public class ShiroRealm extends AuthenticatingRealm{
private static Map<String,User> userMap = new HashMap<String,User>();
static{
//使用Map模拟資料庫擷取User表資訊
userMap.put("jack", new User("jack","aaa123",false));
userMap.put("tom", new User("tom","bbb321",false));
userMap.put("jean", new User("jean","ccc213",true));
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken token) throws AuthenticationException {
//1.把AuthenticationToken轉換為UsernamePasswordToken
UsernamePasswordToken userToken = (UsernamePasswordToken) token;
//2.從UsernamePasswordToken中擷取username
String username = userToken.getUsername();
//3.調用資料庫的方法,從資料庫中查詢Username對應的使用者記錄
System.out.println("從資料看中擷取UserName為"+username+"所對應的資訊。");
//Map模拟資料庫取資料
User u = userMap.get(username);
//4.若使用者不行存在,可以抛出UnknownAccountException
if(u==null){
throw new UnknownAccountException("使用者不存在");
}
//5.若使用者被鎖定,可以抛出LockedAccountException
if(u.isLocked()){
throw new LockedAccountException("使用者被鎖定");
}
//6.根據使用者的情況,來建構AuthenticationInfo對象,通常使用的實作類為SimpleAuthenticationInfo
//以下資訊是從資料庫中擷取的
//1)principal:認證的實體資訊,可以是username,也可以是資料庫表對應的使用者的實體對象
Object principal = u.getUsername();
//2)credentials:密碼
Object credentials = u.getPassword();
//3)realmName:目前realm對象的name,調用父類的getName()方法即可
String realmName = getName();
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(principal,credentials,realmName);
return info;
}
}
可以看到,代碼中并沒有賬号密碼的校驗,那麼Subject對象是如何通過Realm傳回的
AuthenticationInfo對象來進行賬号密碼比對的呢?
通過上面的登入流程我們可以知道,目前有兩個對象是儲存有使用者的賬号和密碼資訊的,一個是
封裝了前台使用者在form表單中填寫的賬号密碼資訊的UsernamePasswordToken,一個是封裝了從
資料庫取出的對應賬号的賬号密碼資訊的SimpleAuthenticationInfo對象。我們可以推測出,
Shiro的密碼比對一定是通過這兩個對象進行比較的,那麼Shiro是什麼時候進行密碼比對的呢?
其實并不難知道Shiro的校驗位置,因為在Shiro進行密碼比對時,一定會去拿UsernamePasswordToken
和SimpleAuthenticationInfo中封裝的密碼資訊,那麼此時要調用UsernamePasswordToken的
getPassword方法,或者調用SimpleAuthenticationInfo的getCredentials方法。
既然這樣,可以關聯UsernamePasswordToken的源代碼,然後找到其getPassword方法,在該處打斷點:
這樣我們重新運作校驗執行個體程式的時候,就可以發現校驗的時機了。
我們在前台輸入賬号密碼點選登入:
然後發現斷點停止了,檢視Debug的視窗,看一下在擷取密碼之前的操作是什麼:
看到前四步的SimpleCredentialsMatcher有一個doCredentialsMatch方法,在該方法中
就進行了密碼比對工作:
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
Object tokenCredentials = getCredentials(token);
Object accountCredentials = getCredentials(info);
return equals(tokenCredentials, accountCredentials);
}
還可以看到,是我們自定義的ShiroRealm(AuthenticatingRealm為父類)執行的
assertCredentialsMatch方法調用的該doCredentialsMatch方法進行密碼比對:
protected void assertCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) throws AuthenticationException {
CredentialsMatcher cm = getCredentialsMatcher();
if (cm != null) {
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父類的自定義
Realm類調用CredentialsMatcher的doCredentialsMatch方法完成的。
目前,我們的密碼比對是直接明文對比的,在實際的生産環境中,密碼往往會進行加密工作,那麼我們如何進行密碼的加密呢?下一篇将介紹如何使用MD5鹽值加密。
轉載請注明出處:http://blog.csdn.net/acmman/article/details/78446008