SaToken學習筆記-04


SaToken學習筆記-04

如果有問題,請點擊:傳送門

角色認證

在sa-token中,角色和權限可以獨立驗證

// 當前賬號是否含有指定角色標識, 返回true或false 
StpUtil.hasRole("super-admin");        

// 當前賬號是否含有指定角色標識, 如果驗證未通過,則拋出異常: NotRoleException 
StpUtil.checkRole("super-admin");        

// 當前賬號是否含有指定角色標識 [指定多個,必須全部驗證通過] 
StpUtil.checkRoleAnd("super-admin", "shop-admin");        

// 當前賬號是否含有指定角色標識 [指定多個,只要其一驗證通過即可] 
StpUtil.checkRoleOr("super-admin", "shop-admin");        

擴展:NotRoleException 對象可通過 getLoginKey() 方法獲取具體是哪個 StpLogic 拋出的異常

源碼解析

- StpUtil.hasRole()
實現了當前賬號是否含有指定角色標識, 返回true或false

/** 
 	 * 當前賬號是否含有指定角色標識, 返回true或false 
 	 * @param role 角色標識
 	 * @return 是否含有指定角色標識
 	 */
 	public static boolean hasRole(String role) {
 		return stpLogic.hasRole(role);
 	}

調用了stpLogic.hasRole()方法並將role傳入

/** 
 	 * 當前賬號是否含有指定角色標識, 返回true或false 
 	 * @param role 角色標識
 	 * @return 是否含有指定角色標識
 	 */
 	public boolean hasRole(String role) {
 		return hasRole(getLoginId(), role);
 	}

將role和獲取的當前會話的loginId傳入給hasRole方法

/** 
 	 * 指定賬號id是否含有角色標識, 返回true或false 
 	 * @param loginId 賬號id
 	 * @param role 角色標識
 	 * @return 是否含有指定角色標識
 	 */
 	public boolean hasRole(Object loginId, String role) {
 		List<String> roleList = SaManager.getStpInterface().getRoleList(loginId, loginKey);
 		return SaManager.getSaTokenAction().hasElement(roleList, role);
//		return !(roleList == null || roleList.contains(role) == false);
 	}

此方法與學習筆記-03中的hasPermission方法類似,首先獲取當前loginId所擁有的所有role的ArrayList ,用roleList接收。再掉用SaTokenAction接口中的hasElement方法進行判斷。SaTokenActionDefaultImpl實現了此接口並且重寫了該方法。

/**
	 * 指定集合是否包含指定元素(模糊匹配) 
	 */
	@Override
	public boolean hasElement(List<String> list, String element) {
		// 集合為空直接返回false
		if(list == null || list.size() == 0) {
			return false;
		}
		// 遍歷匹配
		for (String patt : list) {
			if(SaFoxUtil.vagueMatch(patt, element)) {
				return true;
			}
		}
		// 走出for循環說明沒有一個元素可以匹配成功 
		return false;
	}

對每一個在roleList中的元素與role一一比對,如果相同就返回true,否則就返回false


- StpUtil.checkRole()
實現了當前賬號是否含有指定角色標識, 如果驗證未通過,則拋出異常: NotRoleException
模擬使用場景:

// 當前賬號是否含有指定角色標識, 如果驗證未通過,則拋出異常: NotRoleException
    public boolean checkRole(String role){
        boolean flag = false;
        try {
            StpUtil.checkRole(role);
            flag=true;
        }catch (NotRoleException e){
            String key = e.getLoginKey();
            System.out.println("key:"+key);
        }
        return flag;
    }

對於該方法:

/** 
 	 * 當前賬號是否含有指定角色標識, 如果驗證未通過,則拋出異常: NotRoleException 
 	 * @param role 角色標識
 	 */
 	public static void checkRole(String role) {
 		stpLogic.checkRole(role);
 	}

將role傳入給stpLogic.checkRole方法

/** 
 	 * 當前賬號是否含有指定角色標識, 如果驗證未通過,則拋出異常: NotRoleException 
 	 * @param role 角色標識
 	 */
 	public void checkRole(String role) {
 		if(hasRole(role) == false) {
			throw new NotRoleException(role, this.loginKey);
		}
 	}

調用了hasRole方法(在上面的解析中已經解析過)判斷返回值是否為false,如果為false則表示不包含指定角色,就拋出NotRoleException異常


什么是NotRoleException異常?
/**
 * 沒有指定角色標識,拋出的異常 
 * 
 * @author kong
 *
 */
public class NotRoleException

在這里調用其構造函數

public NotRoleException(String role, String loginKey) {
		// 這里到底要不要拼接上loginKey呢?糾結
		super("無此角色:" + role);
		this.role = role;
		this.loginKey = loginKey;
	}

- StpUtil.checkRoleAnd()
實現了當前賬號是否含有指定角色標識 [指定多個,必須全部驗證通過]
模擬使用場景

//當前賬號是否含有指定角色標識 [指定多個,必須全部驗證通過]
    public boolean checkRoleAnd(String... roles){
        boolean flag = false;
        try
        {
            StpUtil.checkRoleAnd(roles);
            flag = true;
        }catch (NotRoleException e)
        {
            String key = e.getLoginKey();
            System.out.println("key=>"+key);
        }
        return flag;
    }

對於該方法:

/** 
 	 * 當前賬號是否含有指定角色標識 [指定多個,必須全部驗證通過] 
 	 * @param roleArray 角色標識數組
 	 */
 	public static void checkRoleAnd(String... roleArray){
 		stpLogic.checkRoleAnd(roleArray);
 	}

將角色碼數組傳入stpLogic.checkPermissionAnd()方法

/** 
 	 * 當前賬號是否含有指定角色標識 [指定多個,必須全部驗證通過] 
 	 * @param roleArray 角色標識數組
 	 */
 	public void checkRoleAnd(String... roleArray){
 		Object loginId = getLoginId();
 		List<String> roleList = SaManager.getStpInterface().getRoleList(loginId, loginKey);
 		for (String role : roleArray) {
 			if(SaManager.getSaTokenAction().hasElement(roleList, role) == false) {
 				throw new NotRoleException(role, this.loginKey);
 			}
 		}
 	}

與 checkPermissionAnd()方法類似,首先獲取當前對話的loginId並且獲得當前對象的所有角色的ArrayList ,然后通過循環遍歷角色碼數組中的所有元素並且調用hasElement方法與roleList中的所有元素一一對比,如果返回值為false就說明其中不包含該角色,就拋出NotRoleExcetipn異常終止。


- StpUtil.checkRoleOr()
實現了當前賬號是否含有指定角色標識 [指定多個,只要其一驗證通過即可]
模擬使用場景:

//當前賬號是否含有指定角色標識 [指定多個,只要其一驗證通過即可]
    public boolean checkRoleOr(String... roles){
        boolean flag = false;
        try{
            StpUtil.checkRoleOr(roles);
            flag= true;
        }catch (NotRoleException e )
        {
            String key = e.getLoginKey();
            String role = e.getRole();
            System.out.println("key=>"+key+" role=>"+role);
        }
        return true;
    }

對於該方法:

/** 
 	 * 當前賬號是否含有指定角色標識 [指定多個,只要其一驗證通過即可] 
 	 * @param roleArray 角色標識數組
 	 */
 	public static void checkRoleOr(String... roleArray){
 		stpLogic.checkRoleOr(roleArray);
 	}

將角色表示數組傳入給stpLogic.checkRoleOr()

/** 
 	 * 當前賬號是否含有指定角色標識 [指定多個,只要其一驗證通過即可] 
 	 * @param roleArray 角色標識數組
 	 */
 	public void checkRoleOr(String... roleArray){
 		Object loginId = getLoginId();
 		List<String> roleList = SaManager.getStpInterface().getRoleList(loginId, loginKey);
 		for (String role : roleArray) {
 			if(SaManager.getSaTokenAction().hasElement(roleList, role) == true) {
 				// 有的話提前退出
 				return;		
 			}
 		}
		if(roleArray.length > 0) {
	 		throw new NotRoleException(roleArray[0], this.loginKey);
		}
 	}

首先獲取當前對話的loginId,然后用roleList接受帶有所有角色元素的ArrayList ,接着通過for對角色標識數組進行遍歷,並且調用hasElement方法對遍歷的每一個元素判斷是否存在於roleList中,只要有一個能夠匹配的上就立刻提前退出,否則就判斷傳入的角色標識數組長度是否大於0,如果大於0則拋出NotRoleException異常。


權限通配符

Sa-Token允許你根據通配符指定泛權限,例如當一個賬號擁有user*的權限時,user-add、user-delete、user-update都將匹配通過

// 當擁有 user* 權限時
StpUtil.hasPermission("user-add");        // true
StpUtil.hasPermission("user-update");     // true
StpUtil.hasPermission("art-add");         // false

// 當擁有 *-delete 權限時
StpUtil.hasPermission("user-add");        // false
StpUtil.hasPermission("user-delete");     // true
StpUtil.hasPermission("art-delete");      // true

// 當擁有 *.js 權限時
StpUtil.hasPermission("index.js");        // true
StpUtil.hasPermission("index.css");       // false
StpUtil.hasPermission("index.html");      // false

上帝權限:當一個賬號擁有 "*" 權限時,他可以驗證通過任何權限碼 (角色認證同理)


如何把權限精確搭到按鈕級?

權限精確到按鈕級的意思就是指:權限范圍可以控制到頁面上的每一個按鈕是否顯示

思路:如此精確的范圍控制只依賴后端已經難以完成,此時需要前端進行一定的邏輯判斷

在登錄時,把當前賬號擁有的所有權限碼一次性返回給前端
前端將權限碼集合保存在localStorage或其它全局狀態管理對象中
在需要權限控制的按鈕上,使用js進行邏輯判斷,例如在vue框架中我們可以使用如下寫法:

<button v-if="arr.indexOf('user:delete') > -1">刪除按鈕</button>

其中:arr是當前用戶擁有的權限碼數組,user:delete是顯示按鈕需要擁有的權限碼,刪除按鈕是用戶擁有權限碼才可以看到的內容

注意:以上寫法只為提供一個參考示例,不同框架有不同寫法,開發者可根據項目技術棧靈活封裝進行調用


前端有了鑒權后端還需要鑒權嗎?

需要!前端的鑒權只是一個輔助功能,對於專業人員這些限制都是可以輕松繞過的,為保證服務器安全,無論前端是否進行了權限校驗,后端接口都需要對會話請求再次進行權限校驗!


End


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM