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. 17、ansible配置管理

    17.1.前言: 1.说明: ansible是自动化运维工具,基于Python开发,实现了批量系统配置.批量程序部署.批量运行命令等功能. ansible是基于模块工作的,本身没有批量部署的能力,真正 ...

  2. 12、windows定时备份数据库

    12.1.删除指定目录中的内容: del /Q E:\DATABAK\* copy 1.txt bak\a.txt 12.2.可用的备份计划: 1.脚本: BakScripts @echo off R ...

  3. POJ 3087 Shuffle'm Up 模拟,看着不像搜索啊

    题意:给定s1,s1两副扑克,顺序从下到上.依次将s2,s1的扑克一张一张混合.例如s1,ABC; s2,DEF. 则第一次混合后为DAEBFC. 然后令前半段为s1, 后半段为s2. 如果可以变换成 ...

  4. docker搭建数据库高可用方案PXC

    前言 本方案主要目的是学习, 该方案不太合适于企业项目 是什么? 白话点, 是个提供了必要环境的虚拟机(类似于java的导入部分包一样和c++的头文件差不多), 所以它比普通的VMWare或者Virt ...

  5. [转]CURL常用命令

    From:http://www.cnblogs.com/gbyukg/p/3326825.html p.p1 { margin: 0 0 2px; font: 14px ".PingFang ...

  6. mysql学习--MySQL存储引擎对比总结

    一.存储引擎是什么 存储引擎是数据库的核心,对于mysql来说,存储引擎是以插件的形式运行的.MySQL默认配置了许多不同的存储引擎,可以预先设置或者在MySQL服务器中启用.你可以选择适用于服务器. ...

  7. c语言的自动类型转换(转)

    一.自动转换遵循以下规则: 若参与运算量的类型不同,则先转换成同一类型,然后进行运算. 转换按数据长度增加的方向进行,以保证精度不降低.如int型和long型运算时,先把int量转成long型后再进行 ...

  8. 利用C语言判定用户输入数据从而给出结果(利用判定用户体重范围)同求最优解!!!

    例子: 要求:医务工作者通过广泛的调查和统计分析,根据成人的身高与体重因素给出了按"体质指数"进行判断的方法,具体如下: 体质指数t=体重 w/(身高h)2(w的单位为kg,h的单 ...

  9. C语言:extern应用

    前面我们都是将所有的代码写到一个源文件里面,对于小程序,代码不过几百行,这或许无可厚非,但当程序膨胀代码到几千行甚至上万行后,就应该考虑将代码分散到多个文件中,否则代码的阅读和维护将成为一件痛苦的事情 ...

  10. Appearance-Based Loop Closure Detection for Online Large-Scale and Long-Term Operation

    Abstract: 本文提出一种用于大规模的长期回环检测,基于一种内存管理方法:限制用于回环检测的位置数目,以满足实时性要求. introduction: 大场景存在的最关键问题:随着场景增大,回环检 ...