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. codeforces 830E dp

    大致题意: n场比赛,k个钱币.赢一场获得一个钱币,输一场失去一个钱币,一旦钱币数量为2k个或者0个,就马上离开比赛.给出n个长度字符串,由W,D,L,?四个字符组成,W表示赢,L表示输,D表示平局, ...

  2. hdu 2092 整数解(一元二次方程解)

    题目: 思路: 1.两个整数的和和积容易联想到一元二次方程的两个根,只要证明有两个解,并都是整数就打印出Yes,否则打印出No 2.最后判断那步,为什么只需要判断一个整数存在就够了,因为和是整数,一个 ...

  3. OpenResty简介

    OpenResty(也称为 ngx_openresty)是一个全功能的 Web 应用服务器.它打包了标准的 Nginx 核心,很多的常用的第三方模块,以及它们的大多数依赖项. 通过揉和众多设计良好的 ...

  4. css 层叠上下文和层叠顺序

    层叠上下文是css中的一个三维概念,拥有层叠上下文的元素在z轴上比普通元素要高,而且其内部的所有内容是自成体系的,其后代都是在它的层叠顺序中 哪些元素拥有层叠上下文 1.根元素,也就是html默认拥有 ...

  5. 攻防世界-crypto-Decrypt-the-Message(Poem Codes-诗歌密码)

    题目来源:su-ctf-quals-2014题目描述:解密这段信息! 下载附件,内容如下 The life that I have Is all that I have And the life th ...

  6. C语言:赋值

    #include <stdio.h> //=赋值运算符 //具有方向性,只能将赋值号右边的表达式的值给左边的变量 //赋值语句具有计算功能,赋值号右边可以是常量,变量或表达式都可以 //赋 ...

  7. 交换机H3C S3100V2-52TP-WiNet

    H3C S3100V2-52TP-WiNet参数  使用手册三层交换机 默认管理IP:192.168.0.234 将电脑IP设置为192.168.0.0这一组,网络连接交换机,默认用户名密码都为adm ...

  8. 开源桌面快速启动工具-GeekDesk

    GeekDesk 小巧.美观的桌面快速启动工具 开发框架 wpf .net 4.7.2 HandyControl 全局热键 鼠标跟随 快速启动 随时随地 支持自定义热键 支持鼠标跟随 自定义壁纸 随意 ...

  9. 微信小程序云开发-云函数-调用初始云函数获取openid

    一.调用初始云函数获取openid的两种方法 1.传统的success和fail 2.ES6的.then和.catch 3.编译结果 说明:初始云函数,是指刚创建完成的云函数.默认系统写的代码.

  10. springboot+mybatis+mysql 利用mybatis自动生成sql语句

    工具和环境 idea,mysql,JDK1.8 效果图如下 结构图如下 java resources sql文件 /* Navicat MySQL Data Transfer Source Serve ...