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. Typecho 安装教程 -- Linux

    1.下载宝塔面板 1 使用 SSH 连接工具,如堡塔SSH终端连接到您的 Linux 服务器后,挂载磁盘,根据系统执行相应命令开始安装(大约2分钟完成面板安装): 2 Centos安装脚本 yum i ...

  2. POJ 2084 Game of Connections 卡特兰数

    看了下大牛们的,原来这题是卡特兰数,顺便练练java.递归式子:h(0)=1,h(1)=1   h(n)= h(0)*h(n-1) + h(1)*h(n-2) + ... + h(n-1)h(0) ( ...

  3. AcWing 341. 最优贸易

    C国有 n 个大城市和 m 条道路,每条道路连接这 n 个城市中的某两个城市. 任意两个城市之间最多只有一条道路直接相连. 这 m 条道路中有一部分为单向通行的道路,一部分为双向通行的道路,双向通行的 ...

  4. 使用Flyway来管理数据库版本

    使用Flyway来管理数据库版本 Flyway是什么 Flyway是一款数据库迁移(migration)工具. 它可以帮助我们在不同环境保持数据库的同步,减少手工操作,避免数据导入的顺序错误,同时也减 ...

  5. Python实现 利用朴素贝叶斯模型(NBC)进行问句意图分类

    目录 朴素贝叶斯分类(NBC) 程序简介 分类流程 字典(dict)构造:用于jieba分词和槽值替换 数据集构建 代码分析 另外:点击右下角魔法阵上的[显示目录],可以导航~~ 朴素贝叶斯分类(NB ...

  6. 深入理解Java并发类——AQS

    目录 什么是AQS 为什么需要AQS AQS的核心思想 AQS的内部数据和方法 如何利用AQS实现同步结构 ReentrantLock对AQS的利用 尝试获取锁 获取锁失败,排队竞争 参考 什么是AQ ...

  7. java基础---数组的基本概念(1)

    学习资源来自尚硅谷java基础学习 1. 数组的概念 数组(Array), 是多个相同类型数据按一定顺序排列的集合, 并使用一个名字命名, 并通过编号的方式对这些数据进行统一管理. 数组属于引用数据类 ...

  8. C语言:常量写法

    float a=7.5f;  //7.5为浮点数 long b=100L;  //100为长整数 int c=0123;// 0123为8进制数 int d=0x123;//0x123为16进制数

  9. M1卡分类

    M1卡复制前文说到,每一张M1卡的0扇区0块都是出厂时厂商赋予的绝对地址块,我们无法在M1卡内直接修改它. 说到这不得不提一下M1卡的复制子卡--UID卡,FUID卡,CUID卡. UID卡UID卡是 ...

  10. js学习-apply,call,bind的实现

    目录 apply call bind demo apply 简单说:构建一个和调用aplly函数一样的字符串,用eval执行,完了之后删除掉,最后返回执行的结果. Function.prototype ...