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 permissionList = new ArrayList<>();

但是,之前的模拟场景中我们重写了此方法,所以此时应该返回的是带有我们之前定义过的所有权限字段的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类型进行接收。其中的getStpInterface和getPermissionList方法在本文档第一个解析的方法中解析过了,这里就不再解析。接着对获取到的权限列表进行遍历,并且通过调用getSaTokenAction().hasElement(permissionList, permission)对权限进行一一比对,此方法在本文档解析的第一个方法中解析过了,同样不再次解析。如果存在一个权限不存在也就是比对不上,就抛出NotPermissionException异常。结束

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异常,否则就不进行操作,因为表示根本就没有传入相关的权限信息,至此结束。


END

SaToken学习笔记-03的更多相关文章

  1. SaToken学习笔记-04

    SaToken学习笔记-04 如果有问题,请点击:传送门 角色认证 在sa-token中,角色和权限可以独立验证 // 当前账号是否含有指定角色标识, 返回true或false StpUtil.has ...

  2. 机器学习实战(Machine Learning in Action)学习笔记————03.决策树原理、源码解析及测试

    机器学习实战(Machine Learning in Action)学习笔记————03.决策树原理.源码解析及测试 关键字:决策树.python.源码解析.测试作者:米仓山下时间:2018-10-2 ...

  3. OpenCV 学习笔记03 边界框、最小矩形区域和最小闭圆的轮廓

    本节代码使用的opencv-python 4.0.1,numpy 1.15.4 + mkl 使用图片为 Mjolnir_Round_Car_Magnet_300x300.jpg 代码如下: impor ...

  4. OpenCV 学习笔记03 findContours函数

    opencv-python   4.0.1 1 函数释义 词义:发现轮廓! 从二进制图像中查找轮廓(Finds contours in a binary image):轮廓是形状分析和物体检测和识别的 ...

  5. C++ GUI Qt4学习笔记03

    C++ GUI Qt4学习笔记03   qtc++spreadsheet文档工具resources 本章介绍创建Spreadsheet应用程序的主窗口 1.子类化QMainWindow 通过子类化QM ...

  6. SaToken学习笔记-02

    SaToken学习笔记-02 如果排版有问题,请点击:传送门 常用的登录有关的方法 - StpUtil.logout() 作用为:当前会话注销登录 调用此方法,其实做了哪些操作呢,我们来一起看一下源码 ...

  7. SaToken学习笔记-01

    SaToken学习笔记-01 SaToken版本为1.18 如果有排版方面的错误,请查看:传送门 springboot集成 根据官网步骤maven导入依赖 <dependency> < ...

  8. Redis:学习笔记-03

    Redis:学习笔记-03 该部分内容,参考了 bilibili 上讲解 Redis 中,观看数最多的课程 Redis最新超详细版教程通俗易懂,来自 UP主 遇见狂神说 7. Redis配置文件 启动 ...

  9. OGG学习笔记03

    OGG学习笔记03-单向复制简单故障处理 环境:参考:OGG学习笔记02-单向复制配置实例实验目的:了解OGG简单故障的基本处理思路. 1. 故障现象故障现象:启动OGG源端的extract进程,da ...

随机推荐

  1. 2.14、制作Centos模板及优化操作

    1.安装系统: (1)规范网卡为eth0的模式: (2)选择时区: (3)选择支持的语言包含中文: (4)选择软件包: 补充:桌面版包选择: (5)关闭down机内存日志: (6)分区: 1)分区说明 ...

  2. 26、mysqlsla慢查询日志分析工具

    1.介绍: mysqlsla是hackmysql.com推出的一款MySQL的日志分析工具,可以分析mysql的慢查询日志.分析慢查询非常好用,能针 对库分析慢查询语句的执行频率.扫描的数据量.消耗时 ...

  3. PS 快速抠图

    1.选择矩形选框工具-->选择图中要抠掉的地方-->右键填充-->确定

  4. Linux:Linux更新yum方法

    [内容指引]进入目录:cd查看目录下的内容:ls重命名备份:mv从网络下载:wgetyum更新:yum update 第一次运行yum安装软件前,建议更新yum. 1.进入yum源目录 命令: cd ...

  5. Docker:Centos7更新yum源下载docker

    前言: Docker 要求 CentOS 系统(6.5及以上)的内核版本高于 3.10 ,查看本页面的前提条件来验证你的CentOS 版本是否支持 Docker . 通过 uname -r 命令查看你 ...

  6. 记两道最近做的pwn题(ciscn_2019)

    这两题为什么要记录呢,一个是我发现网上很多教程没写清楚(也可能是我太菜了),二是细节点很多,不同的大佬方式不太一样,有很多细节需要注意 ciscn_2019_es_2 这题是栈迁移的题,先上exp 1 ...

  7. python04篇 文件操作(二)、集合

    一.文件操作(二) 1.1 利用with来打开文件 # with open ,python 会自动关闭文件 with open('a.txt', encoding='utf-8') as f: # f ...

  8. Java集合中的可变参数

    可变参数: 1.在JDK1.5之后,如果我们定义一个方法需要接收多个参数,并且多个参数类型一致,我们可以对其简化成如下格式: 修饰符 返回值类型 方法名(参数类型... 形参名){} 其实这个书写完全 ...

  9. deepin解压乱码

    使用unzip命令解压:unzip -O GBK xxxx.zip -d xxx

  10. Python基础之魔术方法(一个序列容器的魔术方法)

    # 创建自己想要的序列容器魔术方法'''__len__():调用len(obj)函数会调用这个魔术方法__getitem__(self,key):在使用下标操作temp['key']以及切片操作的时候 ...