Springboot管理系统数据权限过滤——ruoyi实现方案

本文主要简述,Ruoyi框架使用的权限过滤实现方案,实现简单易懂。主要知识点有:

  1. 注解定义;
  2. 面向切面编程,在执行有数据权限注解的方法之前获取用户组织权限,拼接到domain对象的params参数中;

1. 注解定义

ruoyi数据权限涉及部门表sys_dept和用户表sys_users表,这里仅用来定义sql查询中部门表和用户表的别名。


@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataScope
{/*** 部门表的别名*/public String deptAlias() default "";/*** 用户表的别名*/public String userAlias() default "";/*** 权限字符(用于多个角色匹配符合要求的权限)默认根据权限注解@ss获取,多个权限用逗号分隔开来*/public String permission() default "";
}

2. 注解标记方法拦截后的处理

  1. 查看DataScopeAspect.class源码,添加类注解:@Aspect、@Component。
  2. 权限类型共有五种:超管全部权限(直接跳过权限过滤)、全部数据权限、自定数据权限、部门数据权限、部门及以下数据权限、仅本人数据权限。分别对五种权限进行处理。(DataScope中定义的表别名用途就在这里)

/*** 数据过滤处理** @author ruoyi*/
@Aspect
@Component
public class DataScopeAspect
{/*** 全部数据权限*/public static final String DATA_SCOPE_ALL = "1";/*** 自定数据权限*/public static final String DATA_SCOPE_CUSTOM = "2";/*** 部门数据权限*/public static final String DATA_SCOPE_DEPT = "3";/*** 部门及以下数据权限*/public static final String DATA_SCOPE_DEPT_AND_CHILD = "4";/*** 仅本人数据权限*/public static final String DATA_SCOPE_SELF = "5";/*** 数据权限过滤关键字*/public static final String DATA_SCOPE = "dataScope";@Before("@annotation(controllerDataScope)")public void doBefore(JoinPoint point, DataScope controllerDataScope) throws Throwable{clearDataScope(point);handleDataScope(point, controllerDataScope);}protected void handleDataScope(final JoinPoint joinPoint, DataScope controllerDataScope){// 获取当前的用户LoginUser loginUser = SecurityUtils.getLoginUser();if (StringUtils.isNotNull(loginUser)){SysUser currentUser = loginUser.getUser();// 如果是超级管理员,则不过滤数据if (StringUtils.isNotNull(currentUser) && !currentUser.isAdmin()){String permission = StringUtils.defaultIfEmpty(controllerDataScope.permission(), PermissionContextHolder.getContext());dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(),controllerDataScope.userAlias(), permission);}}}/*** 数据范围过滤** @param joinPoint 切点* @param user 用户* @param deptAlias 部门别名* @param userAlias 用户别名* @param permission 权限字符*/public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String deptAlias, String userAlias, String permission){StringBuilder sqlString = new StringBuilder();List<String> conditions = new ArrayList<String>();for (SysRole role : user.getRoles()){String dataScope = role.getDataScope();if (!DATA_SCOPE_CUSTOM.equals(dataScope) && conditions.contains(dataScope)){continue;}if (StringUtils.isNotEmpty(permission) && StringUtils.isNotEmpty(role.getPermissions())&& !StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission))){continue;}if (DATA_SCOPE_ALL.equals(dataScope)){sqlString = new StringBuilder();conditions.add(dataScope);break;}else if (DATA_SCOPE_CUSTOM.equals(dataScope)){sqlString.append(StringUtils.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias,role.getRoleId()));}else if (DATA_SCOPE_DEPT.equals(dataScope)){sqlString.append(StringUtils.format(" OR {}.dept_id = {} ", deptAlias, user.getDeptId()));}else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope)){sqlString.append(StringUtils.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )",deptAlias, user.getDeptId(), user.getDeptId()));}else if (DATA_SCOPE_SELF.equals(dataScope)){if (StringUtils.isNotBlank(userAlias)){sqlString.append(StringUtils.format(" OR {}.user_id = {} ", userAlias, user.getUserId()));}else{// 数据权限为仅本人且没有userAlias别名不查询任何数据sqlString.append(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias));}}conditions.add(dataScope);}// 多角色情况下,所有角色都不包含传递过来的权限字符,这个时候sqlString也会为空,所以要限制一下,不查询任何数据if (StringUtils.isEmpty(conditions)){sqlString.append(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias));}if (StringUtils.isNotBlank(sqlString.toString())){Object params = joinPoint.getArgs()[0];if (StringUtils.isNotNull(params) && params instanceof BaseEntity){BaseEntity baseEntity = (BaseEntity) params;baseEntity.getParams().put(DATA_SCOPE, " AND (" + sqlString.substring(4) + ")");}}}/*** 拼接权限sql前先清空params.dataScope参数防止注入*/private void clearDataScope(final JoinPoint joinPoint){Object params = joinPoint.getArgs()[0];if (StringUtils.isNotNull(params) && params instanceof BaseEntity){BaseEntity baseEntity = (BaseEntity) params;baseEntity.getParams().put(DATA_SCOPE, "");}}
}

在dataScopeFilter 方法最后的if方法块中,通过向baseEntity中设置dataScope参数,将权限过滤条件放到了查询条件中。

3. 分配数据权限

建立角色后,给角色分配数据权限,再给用户分配一个或多个角色,这样用户即可获取有权限的组织id。这里的问题即是只有支持组织权限,而且组织权限不能应用在不同场景下。
数据权限分配截图:
在这里插入图片描述

4. 权限应用

  1. 在要添加权限的方法体上添加注解并指定部门或人员表别名;且第一个参数是BaseEntity.class类的子类,这样才能设置dataScope查询参数。
    @DataScope(deptAlias = "d")public List<SysDept> selectDeptList(SysDept dept){return deptMapper.selectDeptList(dept);}
  1. sql中添加 ${dataScope}。
<select id="selectAllocatedList" parameterType="SysUser" resultMap="SysUserResult">select distinct u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.phonenumber, u.status, u.create_timefrom sys_user uleft join sys_dept d on u.dept_id = d.dept_idleft join sys_user_role ur on u.user_id = ur.user_idleft join sys_role r on r.role_id = ur.role_idwhere u.del_flag = '0' and r.role_id = #{roleId}<if test="userName != null and userName != ''">AND u.user_name like concat('%', #{userName}, '%')</if><if test="phonenumber != null and phonenumber != ''">AND u.phonenumber like concat('%', #{phonenumber}, '%')</if><!-- 数据范围过滤 -->${params.dataScope}</select>

总结:
1. 逻辑简单,代码实现简洁易懂;
2. 如果还有其他表要控制权限,也可以参考该实现方法,定义注解和拦截实现权限过滤;
3. 当不同场景都要按组织授权,那么目前Ruoyi的实现不能支持;(通过第2个办法:按场景定义不同权限注解,那还需要实现前端配置功能,和相关权限查询功能,还是复杂)

对于企业管理类软件,通常权限控制不止组织,很可能会按其他业务类型控制权限,那如何通过一次编码而且实用于所有业务场景呢?这种系统有以下需求:
1. 通过配置,动态指定权限表(数据库表)或自定义数据类型;可使程序员更关注业务逻辑,减少代码量;
2. 而不侵入SQL代码,仅在方法体上指定相关权限控制编码,可指定表名或字段名;减少对业务侵入;
3. 有多种场景都是按组织分配权限,但不同场景的权限是不一样的,在使用时不能受影响;
4. 应用多样化,可通过方法注释指定使用的权限类型,也可通过前端传参指定权限类型,或者传参指定所有权限。

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

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

相关文章

AI:100-基于卷积神经网络的农作物生长状态监测

🚀 本文选自专栏:人工智能领域200例教程专栏 从基础到实践,深入学习。无论你是初学者还是经验丰富的老手,对于本专栏案例和项目实践都有参考学习意义。 ✨✨✨ 每一个案例都附带有在本地跑过的核心代码,详细讲解供大家学习,希望可以帮到大家。欢迎订阅支持,正在不断更新…

基于CMT2300A定制的模组谐波测量及调试事例

1.1 芯片介绍 CMT2300A华普微推出的一款超低功耗 Sub-1GHz 射频收发器&#xff0c;是一款SPI接口射频前端芯片&#xff0c;调制方式支持OOK (G)FSK 、(G)MSK&#xff0c;速率最大可以做到300 kbps&#xff0c;休眠大概1uA&#xff0c;功率最大可以做到20dB&#xff0c;但各国的…

软文怎么写才能让消费者行动起来?媒介盒子分享

软文的本质是营销&#xff0c;做营销文案不是玩文字艺术&#xff0c;它需要洞察用户需求&#xff0c;懂产品&#xff0c;了解卖点&#xff0c;懂营销&#xff0c;懂消费心理&#xff0c;最终让消费者行动起来。有些文案可能在你看起来遣词造句和配图都很一般&#xff0c;但就是…

Python办公之Excel篇

1.准备环境 Python版本&#xff1a;3.6.5 IDE集成开发环境&#xff1a;pycharm Python库选择&#xff1a;openpyxl openpyxl操作的excel文件以xlsx结尾。 基础命令 查看 Python 版本 python --version查看 pip 版本 pip --version安装openxlsx pip install openpyxl -i…

9.静态路由

静态路由 中小型网络都会用到&#xff0c;防火墙核心交换机用的很多&#xff0c;一般是用在出口 路由表&#xff1a;路由器用来转发数据包唯一的依据 NextHop下一跳 Static静态路由需要手动设置 ip route-static 目标网段 掩码 下一跳例如&#xff1a;ip route-static 192…

QT讲程序打包成安装包让任何人可以使用

&#x1f482; 个人主页:pp不会算法v &#x1f91f; 版权: 本文由【pp不会算法v】原创、在CSDN首发、需要转载请联系博主 &#x1f4ac; 如果文章对你有帮助、欢迎关注、点赞、收藏(一键三连)和订阅专栏哦 文章目录 1、release模式下编译2、windeploy 打包发布3、使用inno setu…

node.js express cors解决跨域

目录 什么是跨域 示例 postman请求 前端请求 cors中间件解决跨域 流程 配置cors参数 什么是跨域 跨域&#xff08;Cross-Origin&#xff09;是指在 Web 开发中&#xff0c;当一个网页的源&#xff08;Origin&#xff09;与另一个网页的源不同时&#xff0c;就发生了跨域…

手把手教你反编译小程序

本次实验环境 操作系统: win10 10.0.19042 node: v14.17.0 微信开发者工具: Stable 1.05.2110290 前期准备 在电脑端安装模拟器工具&#xff0c;这里以夜神模拟器为例&#xff0c; 在模拟器中安装微信&#xff1a;用于微信打开小程序时加载小程序包。在模拟器中文件管理器&…

论文笔记:A review on multi-label learning

一、介绍 传统的监督学习是单标签学习&#xff0c;但是现实中一个实例可能对应多个标签。这篇文章介绍了多标签分类的定义和评价指标、多标签学习的算法还有其他相关的任务。 二、问题相关定义 2.1 多标签学习任务 假设 X R d X R^d XRd&#xff0c;表示d维的输入空间&am…

日本服务器:确保其稳定性的几个要点

​  在租用日本服务器时&#xff0c;用户们大多一定会关注它的稳定性&#xff0c;其实这些顾及都是正常的。毕竟&#xff0c;网站要想正常运行&#xff0c;保障服务器稳定是关键。本文将讨论有关如何保障日本服务器稳定性的一些有用技巧&#xff0c;希望对您有所帮助。 1.注重…

SpringBoot 启动加载器解析

计时器介绍 启动加载器实战 实现方式1 实现CommandLineRunner接口重写run方法通过Order进行排序 示例: Component Order(1) public class FirstCommandlineRunner implements CommandLineRunner {Overridepublic void run(String... args) throws Exception {System.out.pr…

一篇上手机器学习

一、上手机器学习的几个阶段 上手机器学习&#xff0c;第一步当然是看完我的这篇文章啦~&#xff0c;然后就按以下步骤来就可以了&#xff1a; 学习Python编程语言&#xff1a;Python是一种易于学习的高级编程语言&#xff0c;广泛应用于机器学习领域。你可以通过学习Python的…

第三节、项目支付功能实战-微信支付平台接入流程,小程序账号注册、商户注册

简介 本篇介绍小程序的注册流程、商户平台的注册流程、以及小程序和商户平台如何进行绑定。 微信小程序注册 由于项目中使用了小程序进行支付&#xff0c;所以首先来注册小程序。小程序注册网站如下&#xff1a;小程序注册地址 小程序账号注册 1、链接页面点击“前往注册”…

carla安装中的问题

1、carla carla安装完后&#xff0c;需要使用python调用API去更换地图&#xff0c;增加车辆等 使用Python调用API过程中可能会报错&#xff1a; 报错1&#xff1a;carla API&#xff08;Carla包&#xff09;版本不对 **解决方法&#xff1a;**需要将这个目录下的三个文件拷…

数学建模算法

算法部分 1. 评价类模型2. TOPSIS3. 线性规划4. 聚类分析5. 预测模型6. 拉伊达准则(对异常值进行剔除)7. 数据拟合8. 图论代码练习1. 模拟圆周率2. 斐波那契数列3. 四只鸭子落在一个圆中概率4. 方程2: y" uy y,初值y(0) 1,y(0) 0 算法讲解 matlab代码大全 1. 评价类模型…

【Python】修改pip 默认安装位置

使用pip安装的时候&#xff0c;一般是默认安装在c盘里的。这样做很容易会让c盘的文件堆满。那么如何让pip安装的包放入d盘呢&#xff1f; 查看pip默认安装的位置 在cmd里输入python -m site&#xff0c;这里可以看到&#xff0c;安装包会默认下载到c盘中 从这里可以看到&am…

【Spring教程15】Spring框架实战:详解解读AOP的工作流程和AOP的核心概念

目录 1 AOP工作流程2 AOP核心概念 欢迎大家回到《 Java教程之Spring30天快速入门》&#xff0c;本教程所有示例均基于Maven实现&#xff0c;如果您对Maven还很陌生&#xff0c;请移步本人的博文《 如何在windows11下安装Maven并配置以及 IDEA配置Maven环境》&#xff0c;本文…

如何使用cpolar+Inis在Ubuntu系统快速搭建本地博客网站公网可访问

文章目录 前言1. Inis博客网站搭建1.1. Inis博客网站下载和安装1.2 Inis博客网站测试1.3 cpolar的安装和注册 2. 本地网页发布2.1 Cpolar临时数据隧道2.2 Cpolar稳定隧道&#xff08;云端设置&#xff09;2.3.Cpolar稳定隧道&#xff08;本地设置&#xff09; 3. 公网访问测试总…

AspNetCore 中使用 Knife4jUI 更加友好的Swagger界面

&#x1f680;介绍 aspnetcore.knife4j是一个基于.NET Core平台的Swagger UI库&#xff0c;它提供了API文档的生成和管理功能。这个库的前身是swagger-bootstrap-ui&#xff0c;在Java项目中广泛使用&#xff0c;由于其优秀的界面和易用性被许多开发者所推崇。现在&#xff0c…

LV.13 D2 开发板启动流程 学习笔记

一、开发板启动过程 EMMC&#xff1a;相当于电脑的外存&#xff0c;断电不丢失 开发板上电后首先运行SOC内部iROM中固化的代码(BL0)&#xff0c;这段代码先对基本的软硬件环境(时钟等...)进行初始化&#xff0c;然后再检测拨码开关位置获取启动方式&#xff0c;然后再将对应存储…