天天看點

【Shiro權限管理】8.Shiro密碼的比對

上一次總結了如何實作一個簡單的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方法,在該處打斷點:

【Shiro權限管理】8.Shiro密碼的比對

這樣我們重新運作校驗執行個體程式的時候,就可以發現校驗的時機了。

我們在前台輸入賬号密碼點選登入:

【Shiro權限管理】8.Shiro密碼的比對

然後發現斷點停止了,檢視Debug的視窗,看一下在擷取密碼之前的操作是什麼:

【Shiro權限管理】8.Shiro密碼的比對

看到前四步的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

繼續閱讀