【学习】若依源码(前后端分离版)之 “ 用户管理根据不同角色、部门显示数据范围”

news2024/11/23 14:12:24

大型纪录片:学习若依源码(前后端分离版)之 “ 用户管理根据不同角色、部门显示数据范围”

  • 前端部分
  • 后端部分
    • “ /list " 方法
    • " /treeselect " 方法
  • 结语

起因是我想做一个根据不同角色以及其所在的部门展示其相应的信息,只能展示自己部门的信息。后面发现若伊竟然自带了这个功能,不得不说真的强大。它自带了用户管理菜单,里面有角色、部门、用户的模块,其中每个模块又互相有着关系。

用户可以绑定角色,也可以绑定到部门,角色绑定了哪些部门,就决定着隶属于该角色的用户能对哪些部门数据进行操作。

前端部分

图一
图二
那么,怎么实现让用户只能遵循其绑定角色所指定的部门,来进行数据范围控制呢?

具体操作流程如下:

第一步:打开角色管理,选择该角色可以显示的数据有哪些
图三
图四
通过这个操作我们可以实现该角色能看到哪些菜单,能对哪些数据进行操作。那么如果业务进一步需要在角色信息中只显示本部门的数据呢?

第二步:点击 “更多” —》选择 “数据权限”,就可以看到若伊提供的五种权限范围了,这五种权限范围也基本满足我们的业务需求了。
图五
图六
看到这里,相信你就已经会使用若伊用角色和部门进行权限的自定义了。


但是我们的学习还没有结束,这个功能是怎么实现的呢?我们一起来研究一下。首先依旧老一套,我们找到前端的请求代码。

图七
图八
可以看到,在页面加载的时候,就调用了两个方法。 “getList()” 也是老面孔了,前面讲到分页的时候就已经提起过它。那么我们接着看后端。

后端部分

“ /list " 方法

找到代码接收请求的部分,打上断点,进入debug。
图九

图十
家人们,有没有好奇怎么突然多出一条数据??!哪来的??不用慌,我带大家来好好捋一下。

在我们自己做项目的时候怎么实现让用户只能遵循其绑定角色所指定的部门,来进行数据范围控制呢?

一般情况下,假如我们对一张表要进行查询或更新的话,需要在sql语句中,where条件语法后面 加上判断来进行过滤, 例如下面的sql语句:

select * from sys_user
where dept_id = {currentUserDeptId}

但是,在若依框架中,我们只需要在Service层的方法上加入@DataScope注解, 并分别通过deptAliasuserAlias属性,指定出部门表和用户表在sql语句中的别名是什么的话, 就可以灵活地在sql语句后面加上过滤条件了。

下面,我们通过演示,来介绍如何使用@DataScope注解(这里部分参考了网上的回答)。

1.首先,我们有一个部门表的实体类,叫SysDept。并且,它还必须继承了BaseEntity这个类。
图十一

BaseEntity实体类中,有一个类型为map,名称为params的属性。
图十二

2.在对应的mapper.xml文件中,对应sql语句的末尾,我们引用SysUser所继承的BaseEntity父类中的’params’属性 ${params.dataScope}

图十三

3.在Service层方法上,加入@DataScope注解,并指定sql语句中用户表和部门表的别名。
图十四

总结一下 @DataScope(deptAlias = “d”, userAlias = “u”) 这个注解,它是若依框架自定义的一个注解,用于标记需要进行数据范围过滤的方法。运行原理是:

  • 当你在Service层的方法上使用了 @DataScope 注解时,若依框架会通过一个叫做DataScopeAspect的切面类来拦截你的请求,根据你的角色和数据范围权限,动态生成SQL语句,添加数据过滤条件。
  • 这个SQL语句会被赋值给你的实体类所继承的BaseEntity类的params属性,这个属性是一个Map类型,用于存储请求参数。
  • 在Mapper层的XML文件中,你可以通过${params.dataScope}来引用这个SQL语句,作为查询条件的一部分。
  • 这样,就可以实现根据不同的用户角色和数据范围权限,返回不同的数据结果。

现在我们知道了,既然使用了@DataScope后,就会根据前端用户的相关权限参数,自动来生成sql语句用以过滤。 也许你还是会好奇——这个sql语句是在哪儿生成的? 前端用户的权限,肯定也是需要通过响应的判断,来生成的吧,那么在哪儿判断的? 假如它当前自动生成的sql语句,不符合我的现在业务的需求,我怎么去改?

我们打开上面说的DataScopeAspect类来一探究竟。
图十五
还记得之前在前端看到的吗?若伊给数据范围分了五类,原来在这里都有一一对应的。

图十六
打个断点继续测试

图十七
现在我们思路就很清晰了,它会循环判断该用户的角色,然后再获取这个角色的数据显示范围,进行判断找到对应的那个SQL,进行拼接。

你以为到这里就完了吗??哈哈,我们接着往下看,发现又进入了一个判断。

图十八

简单概括一下这个判断有什么作用:

  • 首先,判断sqlString是否为空,sqlString是一个StringBuilder类型的对象,用于存储SQL语句的条件部分。
  • 如果sqlString不为空,那么获取切点的第一个参数,赋值给params对象。切点是指被@DataScope注解标记的方法。
  • 如果params不为空,并且是BaseEntity类型的对象,那么将params强制转换为BaseEntity类型的对象,赋值给baseEntity对象。BaseEntity是若依框架中所有实体类的父类,用于封装公共属性。
  • baseEntity对象中获取params属性,这个属性是一个Map类型,用于存储请求参数。然后将sqlString去掉前面的" AND "字符串,并用括号括起来,作为一个键值对,放入params属性中。键是DATA_SCOPE,值是sqlString。
  • 这样,就可以在Mapper层的XML文件中,通过${params.dataScope}来引用这个SQL语句,作为查询条件的一部分。

" /treeselect " 方法

那么前端发出的第二个请求/treeselect做了哪些事呢,来看源码

    /**
     * 获取部门下拉树列表
     */
    @GetMapping("/treeselect")
    public AjaxResult treeselect(SysDept dept) {
        List<SysDept> depts = deptService.selectDeptList(dept);
        return AjaxResult.success(deptService.buildDeptTreeSelect(depts));
    }

我们发现它跟上一个请求不一样的是,它还多调用了一个方法

先看第一个方法:

    /**
     * 查询部门管理数据
     *
     * @param dept 部门信息
     * @return 部门信息集合
     */
    @Override
    @DataScope(deptAlias = "d")
    public List<SysDept> selectDeptList(SysDept dept) {
        return deptMapper.selectDeptList(dept);
    }

这里其实就跟我们上面讲的那个方法差不多。

再看第二个方法:

    /**
     * 构建前端所需要下拉树结构
     *
     * @param depts 部门列表
     * @return 下拉树结构列表
     */
    @Override
    public List<TreeSelect> buildDeptTreeSelect(List<SysDept> depts) {
        List<SysDept> deptTrees = buildDeptTree(depts);
        return deptTrees.stream().map(TreeSelect::new).collect(Collectors.toList());
    }

解释一下后面这个stream流

  • 首先,调用 deptTrees.stream() 方法,将部门树结构 deptTrees 转换为一个流,这个流中的元素是 SysDept 类型的对象,表示部门节点。
  • 然后,调用 stream().map(TreeSelect::new) 方法,对流中的每个元素进行一个映射操作,将每个 SysDept 对象转换为一个 TreeSelect 对象,并返回一个新的流,这个流中的元素是 TreeSelect 类型的对象,表示下拉树结构中的选项。
  • 最后,调用 stream().collect(Collectors.toList()) 方法,对流进行一个收集操作,将流中的所有元素收集到一个列表中,并返回这个列表。

使用stream流有几个好处:

  1. stream 流可以让我们用一种声明式的方式来处理集合或数组中的数据,而不需要写很多繁琐的循环和判断。stream 流可以让我们专注于数据的变化和操作,而不是数据的存储和遍历。
  2. stream 流可以提供一些高级的功能,如并行处理、延迟执行、短路求值等,这些功能可以提高代码的性能和效率。
  3. stream 流可以提高代码的可读性和可维护性,因为它使用了一些函数式编程的概念和方法,如 lambda 表达式、方法引用、函数接口等,这些概念和方法可以让代码更简洁和清晰。

问:那他们两个类字段也不一样啊,怎么对上的?

答:它是通过调用 TreeSelect 的构造方法来实现的。在TreeSelect 的构造方法有一段这样的定义:

public TreeSelect(SysDept dept) {
    this.id = dept.getDeptId();
    this.label = dept.getDeptName();
    this.children = dept.getChildren().stream().map(TreeSelect::new).collect(Collectors.toList());
}

再看这个buildDeptTree(depts);方法,相信大家都不陌生了,前面讲动态路由树的时候详细讲过,这里就把相关的一小节代码贴出来,总体递归思路和前面那个生成动态路由树基本一致。

/**
     * 构建前端所需要树结构
     *
     * @param depts 部门列表
     * @return 树结构列表
     */
    @Override
    public List<SysDept> buildDeptTree(List<SysDept> depts) {
        List<SysDept> returnList = new ArrayList<SysDept>();
        List<Long> tempList = new ArrayList<Long>();
        //把所有部门的id取出来,方便后面进行父节点比较
        for (SysDept dept : depts) {
            tempList.add(dept.getDeptId());
        }
        for (Iterator<SysDept> iterator = depts.iterator(); iterator.hasNext(); ) {
            SysDept dept = (SysDept) iterator.next();
            // 如果是顶级节点, 遍历该父节点的所有子节点
            if (!tempList.contains(dept.getParentId())) {
                recursionFn(depts, dept);
                returnList.add(dept);
            }
        }
        if (returnList.isEmpty()) {
            returnList = depts;
        }
        return returnList;
    }

结语

好了,解释完这个切面我们也就懂了他是怎么实现根据不同角色的不同职位做不同的数据范围了。希望你能从文章中学到、了解一下东西。欢迎留言评论一起交流心得。

今天的篇幅有点长,那么以上就是唐某的一些理解。这次的分享就到这里了。记得一键三连~( •̀ ω •́ )✧

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/860826.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

StarRocks 3.1重磅发布,云原生湖仓新范式再升级!

StarRocks 自4月底发布3.0版本&#xff0c;拥抱云原生&#xff0c;开启极速统一的湖仓新范式&#xff1b;8月7日&#xff0c;StarRocks 正式发布全新3.1版本&#xff0c;全面提升云原生存算分离构架、极速数据湖分析、物化视图等重量级特性&#xff0c;让用户更简单的实现极速统…

前端性能优化之性能优化的指标和工具(chrome devtools、lighthouse、webpagetest)

文章目录 引言一、为什么要进行web性能优化二、RAIL测量模型1. 什么是RAIL2. 性能测量工具 三、性能测量工具的使用和性能指标以及优化目标1. Chrome DevTools1. 打开调试工具方式和配置2. network下的几个性能指标1. requests 请求总数2. transferred实际从服务器下载的数据量…

uni-app:实现点击按钮,进行数据累加展示(解决数据过多,导致出错)

效果 代码 核心代码 一、标签显示 <!-- 加载更多 --> <view class"load_more" v-if"info.length > pageNum * pageSize" tap"loadMore">加载更多 </view> v-if"info.length > pageNum * pageSize"&#xf…

主数据管理案例-某研究所

1、 背景介绍及难点分析 某军工研究所是机电类科研生产一体化研究所&#xff0c;具有多品种、小批量、离散性、央企、军工保密等特点&#xff0c;在数据管理系统和研制管理体系的控制下&#xff0c;设计、工艺、 制造、试验、售后服务等环节都产生了大量的数据。在管理信息化、…

影像维修工程师专项技能培训

最近遇到很多咨询的人员都在对医疗行业产生疑惑&#xff0c;新闻报道说很多医院、公司的领导都被查&#xff0c;这样会不会影响设备维修方面&#xff0c;对后期找工作等有没有影响&#xff1f;总不能学好了技术却没有发挥的余地&#xff1f; 最近确实是国家整体在对医疗方面做…

Win11 VS2022 配置CGAL-5.6

由于项目要用到几何库CGAL&#xff0c;因此做了配置。采用的是官方文档中的“Installing from the Source Archive”方式。 1. 下载安装CGAL &#xff08;1&#xff09;CGAL-5.6.zip下载地址&#xff1a;Releases CGAL/cgal GitHub 下载下图所示的两个文件。 &#xff08…

LeetCode面向运气之Javascript—第27题-移除元素-98.93%

LeetCode第27题-移除元素 题目要求 一个数组nums和一个值val&#xff0c;你需要原地移除所有数值等于val的元素&#xff0c;并返回移除后数组的新长度 举例 输入&#xff1a;nums [3,2,2,3], val 3 输出&#xff1a;2, nums [2,2] 输入&#xff1a;nums [0,1,2,2,3,0,4,2…

【C++】——模板

目录 泛型编程函数模板函数模板的概念函数模板格式&#xff1a;函数模板的原理函数模板的实例化模板参数的匹配原则 类模板类模板定义格式类模板的实例化 泛型编程 泛型编程&#xff1a;编写与类型无关的通用代码&#xff0c;是代码复用的一种手段。模板是泛型编程的基础 引例…

并发——线程池实践

文章目录 1. 使用 ThreadPoolExecutor 的构造函数声明线程池2.监测线程池运行状态3.建议不同类别的业务用不同的线程池4.别忘记给线程池命名5.正确配置线程池参数常规操作美团的骚操作 简单总结一下我了解的使用线程池的时候应该注意的东西&#xff0c;网上似乎还没有专门写这…

带你了解科研院所

一、什么是科研院所 研究院是独立于教育部和高校系统之外的&#xff0c;以科研工作为业务核心的各级、各类研究机构。独立研究院有很多种&#xff0c;其中实力最强、名气最大、分布最广、数量最集中的是直属国务院的中科院、社科院两大科研系统中的各类研究所和研究中心。 两大…

大数据课程I1——Kafka的概述

文章作者邮箱&#xff1a;yugongshiyesina.cn 地址&#xff1a;广东惠州 ▲ 本章节目的 ⚪ 了解Kafka的概念&#xff1b; ⚪ 掌握Kafka的配置与启动&#xff1b; 一、简介 1. 基本概念 Apache kafka 是一个分布式数据流平台。可以从如下几个层面来理解&#x…

第十六章、【Linux】程序管理与SELinux初探

16.1 什么是程序 &#xff08;process&#xff09; 在Linux 系统当中&#xff1a;“触发任何一个事件时&#xff0c;系统都会将他定义成为一个程序&#xff0c;并且给予这个程序一个 ID &#xff0c;称为 PID&#xff0c;同时依据启发这个程序的使用者与相关属性关系&#xff…

Ubuntu18.04使用carla0.9.5联合仿真搭环境报错

Ubuntu18.04使用工程与carla0.9.5联合仿真报错 1 File "/home/cg/Auto_driving/src/ros-bridge/carla_ros_bridge/src/carla_ros_bridge/client.py", line 18, in <module>from carla_ros_bridge.bridge_with_rosbag import CarlaRosBridgeWithBagFile "…

04.利用Redis国逻辑过期实现缓存功能---解决缓存击穿

学习目标&#xff1a; 提示&#xff1a;学习如何利用Redis逻辑过期实现添加缓存功能解决缓存击穿 学习产出&#xff1a; 缓存击穿讲解图&#xff1a; 解决方案&#xff1a; 采用互斥锁采用逻辑过期 1. 准备pom环境 <dependency><groupId>org.springframework…

webpack 创建VUE项目

1、安装 node.js 下载地址&#xff1a;https://nodejs.org/en/ 下载完成以后点击安装&#xff0c;全部下一步即可 安装完成&#xff0c;输入命令验证 node -vnpm -v2.搭建VUE环境 输入命令&#xff0c;全局安装 npm install vue-cli -g安装完成后输入命令 查看 vue --ver…

算法篇之(Map Set)

前言&#xff1a;前面学习了List线性表的数组、链表数据结构&#xff0c;本篇博客主要学习和List相似的数据结构&#xff1a;Map和Set。 目录 思维导图 有效字母异位词 两数之和 思维导图 有效字母异位词 可以用哈希表实现 先创建哈希表&#xff0c; dic1{}对每个字符串进…

【C语言学习】条件运算符、逻辑运算、运算符优先级

一、条件运算符 条件&#xff1f;条件满足时的值&#xff1a;条件不满足时的值 count (count>20)?count-10:count10;等同于 if( count>20 )count count-10; elsecount count10; 优先级 条件运算符的优先级高于赋值运算符&#xff0c;但低于其他运算符。 尽量不要…

k8s 自身原理 2

前面我们说到 K8S 的基本原理和涉及的四大组件&#xff0c;分享了前两个组件 etcd 和 ApiServer 这一次我们接着分享一波&#xff1a; 调度器 scheduler控制器管理器 controller manager 调度器 scheduler 调度器&#xff0c;见名知意&#xff0c;用于调度 k8s 资源的&…

复现Cell图表:pyscenic分析之转录因子二项值热图

接上一节视频教程的分析结果(pyscenic分析&#xff1a;视频教程)。今天我们复现一篇cell子刊的图表&#xff0c;这篇文章有一副关于转录因子的图表&#xff0c;观察这个图有什么特点呢&#xff1f;第一是热图是二项值热图&#xff0c;只有0&#xff0c;1两个值&#xff0c;我们…

Rikka with Square Numbers 2023“钉耙编程”中国大学生算法设计超级联赛(8)hdu7370

Problem - 7370 题目大意&#xff1a;给出两个数a&#xff0c;b&#xff0c;每次操作可以使其中一个数加上或减去一个任意的完全平方数&#xff0c;问要使a&#xff0c;b相等需要的最少操作次数是多少 1<a,b<1e9,a!b 思路&#xff1a;我们可以将问题转化为将a和b的差w…