SaToken學習筆記-03
如果排版有問題,請點擊:傳送門
核心思想
所謂權限驗證,驗證的核心就是一個賬號是否擁有一個權限碼
有,就讓你通過。沒有?那么禁止訪問!
再往底了說,就是每個賬號都會擁有一個權限碼集合,我來驗證這個集合中是否包含指定的權限碼
例如:當前賬號擁有權限碼集合:["user-add", "user-delete", "user-get"],這時候我來驗證權限 "user-update",則其結果就是:驗證失敗,禁止訪問
所以現在問題的核心就是:
如何獲取一個賬號所擁有的的權限碼集合
本次操作需要驗證的權限碼是哪個
模擬使用場景
准備工作:
package com.pj.satoken;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Component;
import cn.dev33.satoken.stp.StpInterface;
/**
* 自定義權限驗證接口擴展
*/
@Component // 保證此類被SpringBoot掃描,完成sa-token的自定義權限驗證擴展
public class StpInterfaceImpl implements StpInterface {
/**
* 返回一個賬號所擁有的權限碼集合
*/
@Override
public List<String> getPermissionList(Object loginId, String loginKey) {
// 本list僅做模擬,實際項目中要根據具體業務邏輯來查詢權限
List<String> list = new ArrayList<String>();
list.add("101");
list.add("user-add");
list.add("user-delete");
list.add("user-update");
list.add("user-get");
list.add("article-get");
return list;
}
/**
* 返回一個賬號所擁有的角色標識集合 (權限與角色可分開校驗)
*/
@Override
public List<String> getRoleList(Object loginId, String loginKey) {
// 本list僅做模擬,實際項目中要根據具體業務邏輯來查詢角色
List<String> list = new ArrayList<String>();
list.add("admin");
list.add("super-admin");
return list;
}
}
准備完畢,可以使用相關api
權限驗證api
// 當前賬號是否含有指定權限, 返回true或false
StpUtil.hasPermission("user-update");
// 當前賬號是否含有指定權限, 如果驗證未通過,則拋出異常: NotPermissionException
StpUtil.checkPermission("user-update");
// 當前賬號是否含有指定權限 [指定多個,必須全部驗證通過]
StpUtil.checkPermissionAnd("user-update", "user-delete");
// 當前賬號是否含有指定權限 [指定多個,只要其一驗證通過即可]
StpUtil.checkPermissionOr("user-update", "user-delete");
擴展:NotPermissionException 對象可通過 getLoginKey() 方法獲取具體是哪個 StpLogic 拋出的異常
源碼解析
StpUtil.hasPermission("user-update");
進入第一層
/**
* 當前賬號是否含有指定權限, 返回true或falsquae
* @param permission 權限碼
* @return 是否含有指定權限
*/
public static boolean hasPermission(String permission) {
return stpLogic.hasPermission(permission);
}
將傳入的權限名稱傳入調用的stpLogic.hasPermission(permission)方法
繼續進入
/**
* 當前賬號是否含有指定權限, 返回true或false
* @param permission 權限碼
* @return 是否含有指定權限
*/
public boolean hasPermission(String permission) {
return hasPermission(getLoginId(), permission);
}
將獲取的loginId和傳入的權限名稱一起傳入調用的hasPermission方法
下一步
/**
* 指定賬號id是否含有指定權限, 返回true或false
* @param loginId 賬號id
* @param permission 權限碼
* @return 是否含有指定權限
*/
public boolean hasPermission(Object loginId, String permission) {
List<String> permissionList = SaManager.getStpInterface().getPermissionList(loginId, loginKey);
return SaManager.getSaTokenAction().hasElement(permissionList, permission);
// return !(permissionList == null || permissionList.contains(permission) == false);
}
創建了一個list來接受SaManager.getStpInterface().getPermissionList(loginId, loginKey)返回的ArrayList
先看什么是getStpInterface()
public static StpInterface getStpInterface() {
if (stpInterface == null) {
synchronized (SaManager.class) {
if (stpInterface == null) {
setStpInterface(new StpInterfaceDefaultImpl());
}
}
}
return stpInterface;
}
很簡單就是創建並且返回了一個stpInterface接口的實現類StpInterfaceDefaultImpl
接下來調用了實現類中的getPermissionList方法
@Override
public List<String> getPermissionList(Object loginId, String loginKey) {
return new ArrayList<String>();
}
可以看到其實跟傳入的值沒有關系,就是簡單的返回了一個ArrayList
所以第一個操作基本等同於
List
但是,之前的模擬場景中我們重寫了此方法,所以此時應該返回的是帶有我們之前定義過的所有權限字段的list集合
ok,下一個操作我們先看getStpInterface()
public static SaTokenAction getSaTokenAction() {
if (saTokenAction == null) {
synchronized (SaManager.class) {
if (saTokenAction == null) {
setSaTokenAction(new SaTokenActionDefaultImpl());
}
}
}
return saTokenAction;
}
和上面的getStpInterface()類似,同樣是創建並且返回了一個SaTokenAction的實現類SaTokenActionDefaultImpl
然后調用了實現類中的hasElement方法
/**
* 指定集合是否包含指定元素(模糊匹配)
*/
@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;
}
可以看到首先判斷之前得到的權限集合是否為空,若是的話則代表此用戶沒有任何權限,直接返回false,表示沒有權限
之后,通過遍歷集合的方式將每個元素和傳入的要查找的權限字符串進行對比,此對比底層為模糊比對,如果成功則表示確實有這項權限返回true,否則返回false,代表沒有此項權限
StpUtil.checkPermission("user-update");
判斷當前賬號是否含有指定權限, 如果驗證未通過,則拋出異常: NotPermissionException
使用場景:
@RequestMapping("/checkPermission")
public boolean CheckPermission(@RequestParam("limit")String limit){
boolean flag = true;
try
{
StpUtil.checkPermission(limit);
}catch (NotPermissionException e){
flag = false;
String key = e.getLoginKey();
String code = e.getCode();
System.out.println("key=>"+key+",code=>"+code);
}
return flag;
}
開始查看源碼:
/**
* 當前賬號是否含有指定權限, 如果驗證未通過,則拋出異常: NotPermissionException
* @param permission 權限碼
*/
public static void checkPermission(String permission) {
stpLogic.checkPermission(permission);
}
將傳入的權限字符串傳入調用的stpLogic.checkPermission方法中,繼續下一步
/**
* 當前賬號是否含有指定權限, 如果驗證未通過,則拋出異常: NotPermissionException
* @param permission 權限碼
*/
public void checkPermission(String permission) {
if(hasPermission(permission) == false) {
throw new NotPermissionException(permission, this.loginKey);
}
}
其中調用了hasPermission方法,與上面方法調用的hasPermission方法一致,不做解析
如果返回false則說明沒有這項權限也就是驗證未通過,就拋出NotPermissionException異常,結束
什么是NotPermissionException?
/**
* 沒有指定權限碼,拋出的異常
*
* @author kong
*
*/
public class NotPermissionException extends SaTokenException
StpUtil.checkPermissionAnd("user-update", "user-delete")
當前賬號是否含有指定權限 [指定多個,必須全部驗證通過]
使用場景:
@RequestMapping("checkPermissionAnd")
public boolean checkPermissionAnd(@RequestParam("limits")String... limits){
boolean flag = true;
try{
StpUtil.checkPermissionAnd(limits);
}catch (NotPermissionException e){
flag=false;
String key = e.getLoginKey();
String code = e.getCode();
System.out.println("key=>"+key+",code=>"+code);
}
return flag;
}
開始瀏覽源碼
/**
* 當前賬號是否含有指定權限 [指定多個,必須全部驗證通過]
* @param permissionArray 權限碼數組
*/
public static void checkPermissionAnd(String... permissionArray) {
stpLogic.checkPermissionAnd(permissionArray);
}
什么是String...?
類型后面三個點(String…),是從Java 5開始,Java語言對方法參數支持一種新寫法,叫可變長度參數列表,其語法就是類型后跟…,表示此處接受的參數為0到多個Object類型的對象,或者是一個Object[]。 例如我們有一個方法叫做test(String…strings),那么你還可以寫方法test(),但你不能寫test(String[] strings),這樣會出編譯錯誤,系統提示出現重復的方法。
在使用的時候,對於test(String…strings),你可以直接用test()去調用,標示沒有參數,也可以用去test(“aaa”),也可以用test(new String[]{“aaa”,”bbb”})。
另外如果既有test(String…strings)函數,又有test()函數,我們在調用test()時,會優先使用test()函數。只有當沒有test()函數式,我們調用test(),程序才會走test(String…strings)。
將permissionArray傳入調用的checkPermissionAnd方法
/**
* 當前賬號是否含有指定權限 [指定多個,必須全部驗證通過]
* @param permissionArray 權限碼數組
*/
public void checkPermissionAnd(String... permissionArray){
Object loginId = getLoginId();
List<String> permissionList = SaManager.getStpInterface().getPermissionList(loginId, loginKey);
for (String permission : permissionArray) {
if(SaManager.getSaTokenAction().hasElement(permissionList, permission) == false) {
throw new NotPermissionException(permission, this.loginKey);
}
}
}
不難看出首先獲取到loginId,然后通過調用SaManager.getStpInterface().getPermissionList(loginId, loginKey)獲取到該loginId的所有權限列表並且用List
StpUtil.checkPermissionOr("user-update", "user-delete")
實現了當前賬號是否含有指定角色標識 [指定多個,只要其一驗證通過即可]
使用場景:
@RequestMapping("checkPermissionOr")
public boolean checkPermissionOr(@RequestParam("limits")String... limits){
boolean flag = true;
try{
StpUtil.checkPermissionOr(limits);
}catch (NotPermissionException e){
flag=false;
String key = e.getLoginKey();
String code = e.getCode();
System.out.println("key=>"+key+",code=>"+code);
}
return flag;
}
開始瀏覽源碼
/**
* 當前賬號是否含有指定權限 [指定多個,只要其一驗證通過即可]
* @param permissionArray 權限碼數組
*/
public static void checkPermissionOr(String... permissionArray) {
stpLogic.checkPermissionOr(permissionArray);
}
與chekPermissionAnd方法相似,調用了stpLogic.checkPermissionOr方法並將權限信息傳入。
/**
* 當前賬號是否含有指定權限 [指定多個,只要其一驗證通過即可]
* @param permissionArray 權限碼數組
*/
public void checkPermissionOr(String... permissionArray){
Object loginId = getLoginId();
List<String> permissionList = SaManager.getStpInterface().getPermissionList(loginId, loginKey);
for (String permission : permissionArray) {
if(SaManager.getSaTokenAction().hasElement(permissionList, permission) == true) {
// 有的話提前退出
return;
}
}
if(permissionArray.length > 0) {
throw new NotPermissionException(permissionArray[0], this.loginKey);
}
}
發現其中的大部分操作都與checkPermissionAnd中類似。在遍歷判斷權限時條件變為如果有一個權限比對上了就提前退出。如果沒有退出就說明沒有任何一個權限比對成功,最后判斷傳入的權限信息是否存在,如果存在則說明傳入的權限信息都不符合,就拋出NotPermissionException異常,否則就不進行操作,因為表示根本就沒有傳入相關的權限信息,至此結束。
