SaToken学习笔记-04

时间:2021-08-11
本文章向大家介绍SaToken学习笔记-04,主要包括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

原文地址:https://www.cnblogs.com/yangchen-geek/p/15128812.html