java 后端数据权限解读

一、代码

package com.dkd.framework.aspectj;import com.dkd.common.annotation.DataScope;
import com.dkd.common.core.domain.BaseEntity;
import com.dkd.common.core.domain.entity.SysRole;
import com.dkd.common.core.domain.entity.SysUser;
import com.dkd.common.core.domain.model.LoginUser;
import com.dkd.common.core.text.Convert;
import com.dkd.common.utils.SecurityUtils;
import com.dkd.common.utils.StringUtils;
import com.dkd.framework.security.context.PermissionContextHolder;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;import java.util.ArrayList;
import java.util.List;/*** 数据过滤处理** @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{// 清理数据范围(权限)过滤条件(params.dataScope)防止sql注入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()){// 获取目标方法的权限字符串  例如:用户列表(system:user:list)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){// 构建SQL字符串,用于拼接数据范围条件StringBuilder sqlString = new StringBuilder();// 用于存储已经添加的数据范围类型,避免重复添加List<String> conditions = new ArrayList<String>();// 遍历用户的所有角色for (SysRole role : user.getRoles()){// 获取当前角色的数据范围条件类型 1~5String dataScope = role.getDataScope();// 如果数据范围类型是自定义类型且已添加,则跳过本次循环if (!DATA_SCOPE_CUSTOM.equals(dataScope) && conditions.contains(dataScope)){continue;}// 如果当前角色权限列表不包含目标方法的权限字符串(system:user:list),则跳过本次循环if (StringUtils.isNotEmpty(permission) && StringUtils.isNotEmpty(role.getPermissions())&& !StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission))){continue;}// 如果角色的数据范围类型是全部数据,则清空SQL字符串并添加数据范围类型,结束循环if (DATA_SCOPE_ALL.equals(dataScope)){sqlString = new StringBuilder();conditions.add(dataScope);break;}// 如果角色的数据范围类型是自定义数据else if (DATA_SCOPE_CUSTOM.equals(dataScope)){// 拼接SQL条件,限制部门ID在角色所关联的部门范围内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)){// 拼接SQL条件,限制部门ID等于用户所在部门IDsqlString.append(StringUtils.format(" OR {}.dept_id = {} ", deptAlias, user.getDeptId()));}// 如果角色的数据范围类型是本部门及子部门数据else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope)){// 拼接SQL条件,限制部门ID等于用户所在部门ID或在用户所在部门的子孙部门中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)){// 如果用户表别名不为空,拼接SQL条件限制用户ID等于当前用户IDif (StringUtils.isNotBlank(userAlias)){sqlString.append(StringUtils.format(" OR {}.user_id = {} ", userAlias, user.getUserId()));}else{ // 否则,拼接SQL条件限制部门ID为0,即不查询任何数据sqlString.append(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias));}}// 添加当前角色的数据范围类型条件conditions.add(dataScope);}// 如果数据范围类型集合(即多角色情况下,所有角色都不包含传递过来目标方法的权限字符),则添加一个条件使SQL查询不返回任何数据if (StringUtils.isEmpty(conditions)){sqlString.append(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias));}// 如果SQL字符串不为空,则将构造好的数据范围条件添加到方法参数对象中,用于后续的SQL查询if (StringUtils.isNotBlank(sqlString.toString())){// 获取切点方法的第一个参数Object params = joinPoint.getArgs()[0];// 检查参数是否非空且为BaseEntity类型if (StringUtils.isNotNull(params) && params instanceof BaseEntity){// 将参数转换为BaseEntity类型BaseEntity baseEntity = (BaseEntity) params;// 向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];// 检查参数不为null且是否为BaseEntity类型if (StringUtils.isNotNull(params) && params instanceof BaseEntity){// 将参数转为BaseEntity类型BaseEntity baseEntity = (BaseEntity) params;// 将数据权限过滤条件设置为空baseEntity.getParams().put(DATA_SCOPE, "");}}
}

二、代码使用

(一)、核心功能

1.数据权限控制

根据用户的权限和角色,动态生成 SQL 查询条件,确保用户只能看到自己有权访问的数据。

2.AOP 实现

使用 AspectJ 实现 AOP,通过 @Before 注解在方法执行前进行数据权限过滤。

3.多种数据范围类型

支持多种数据范围类型,包括全部数据、自定义数据、部门数据、部门及子部门数据、仅本人数据等

(二)、基本配置

@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 注解

@Before("@annotation(controllerDataScope)")
public void doBefore(JoinPoint point, DataScope controllerDataScope) throws Throwable {// 清理数据范围(权限)过滤条件(params.dataScope)防止sql注入clearDataScope(point);// 设置数据范围(权限)过滤条件handleDataScope(point, controllerDataScope);
}

这个方法在带有 @DataScope 注解的方法执行前被调用,主要负责清理和设置数据范围过滤条件。

(四)、handleDataScope 方法

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()) {// 获取目标方法的权限字符串  例如:用户列表(system:user:list)String permission = StringUtils.defaultIfEmpty(controllerDataScope.permission(), PermissionContextHolder.getContext());// 设置数据范围(权限)过滤条件dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(),controllerDataScope.userAlias(), permission);}}
}

该方法首先获取当前登录用户的信息,然后检查是否为超级管理员。如果不是超级管理员,则根据用户的角色和权限生成 SQL 查询条件。

(五)、dataScopeFilter 方法

public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String deptAlias, String userAlias, String permission) {// 构建SQL字符串,用于拼接数据范围条件StringBuilder sqlString = new StringBuilder();List<String> conditions = new ArrayList<>();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 {sqlString.append(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias));}}conditions.add(dataScope);}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 查询条件,并将其添加到方法参数对象中。具体逻辑如下:
遍历用户的所有角色,根据每个角色的数据范围类型生成 SQL 条件。
最终生成的 SQL 条件会被添加到 BaseEntity 的 params 属性中,用于后续的 SQL 查询。

(六)、clearDataScope 方法

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, "");}
}

该方法用于清除 BaseEntity 中的数据范围条件,防止 SQL 注入攻击。

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

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

相关文章

Frontiers出版社系列SCISSCI合集

【SciencePub学术】本期&#xff0c;小编根据WOS数据库&#xff0c;整理了一下Frontiers出版社系列的SCI&SSCI合集&#xff0c;以供各位学者投稿参考&#xff01; 来源&#xff1a;WOS数据库 Frontiers系列期刊中&#xff0c;Frontiers in Immunology以其5.7分的影响因子位…

st7735调试记录

由于该模块陪我已经超过十年&#xff0c;最近想起来学习下lvgl&#xff0c;于是乎将其拿出来&#xff0c;尝试使能该模块从而学会lvgl 第一步肯定是找到资料确定下该模块是否好用。于是到网络上找到如下资料进行验证&#xff1a; https://pan.baidu.com/s/1CEunLuGUqLABR6I0UZ…

海外云手机在电商运营中的优势解析

近年来&#xff0c;海外云手机被越来越多人熟知&#xff0c;凭借云计算与电子商务的结合&#xff0c;成为出海电商企业高效、灵活的运营工具。本文将从多个角度详细解析海外云手机在电商运营中的主要优势。 一、成本节约 相比传统出海电商所依赖的实体设备如手机和电脑&#xf…

RPC框架开发——理解项目功能

目录 一、RPC的概念 二、使用分布式架构 三、进一步改进 四、最终框架 一、RPC的概念 RPC&#xff08;Remote Procedure Call&#xff0c;远程过程调用&#xff09;是一种通信机制&#xff0c;使得客户端能够像调用本地函数一样调用远程服务器上的函数。本质上是客户端需要…

均衡功能,保障安全丨基于极海 G32A1445 汽车通用 MCU 的 BMU 应用方案

BMS电池管理系统是每个电动汽车车企不断优化改进的应用产品&#xff0c;其组成中的BMU用于实现电流检测、绝缘检测、SOC估算、容量累积、报警功能、充放电管理、远程监控等功能。BMU组成包括微控制器系统、充放电管理单元、CAN通信网络单元&#xff08;采集所有从控单体电池信息…

螺丝头与螺杆检测系统源码分享

螺丝头与螺杆检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer…

如何使用ssm实现基于ssm的高校大学生党建系统

TOC ssm688基于ssm的高校大学生党建系统jsp 绪论 1.1 研究背景 当前社会各行业领域竞争压力非常大&#xff0c;随着当前时代的信息化&#xff0c;科学化发展&#xff0c;让社会各行业领域都争相使用新的信息技术&#xff0c;对行业内的各种相关数据进行科学化&#xff0c;规…

Vue学习记录之八(局部组件,全局组件,递归组件,动态组件)

一、局部组件 在src\components\Card.vue 建立一个文件&#xff0c;代码如下&#xff1a; <template><div class"card"><header><div>标题</div><div>副标题</div></header><section>内容</section>&…

2024软件测试面试八股文(有答案版),金九银十季,涨薪涨薪_八股文软件测试面试题

因为我们项目的用户活动和三方合作平台比较多&#xff0c;一般半个月或者1个月肯定会有一个迭代版本&#xff0c; 假如用户或者合作方突然有很紧急的需求&#xff0c;那一般老大他们会向上发邮件和OA呈批给(产品经理&#xff0c;项目经理)&#xff0c;如果通过了就会马上加急处…

【Go语言】深入解读Go语言中的指针,助你拨开迷雾见月明

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

《操作系统 - 清华大学》1 -1:操作系统概述 —— 内容概述

文章目录 1. 内容摘要2. 实验内容 1. 内容摘要 在这里对学习内容做一个整体上的介绍&#xff0c;那在这里包括我们要学习的内容&#xff0c;实验的内容。操作系统课涉及到计算机系统当中的资源管理&#xff0c;所以我们围绕着操作系统的实现来介绍相关内容&#xff0c;那主要分…

优化 Go 语言数据打包:性能基准测试与分析

场景&#xff1a;在局域网内&#xff0c;需要将多个机器网卡上抓到的数据包同步到一个机器上。 原有方案&#xff1a;tcpdump -w 写入文件&#xff0c;然后定时调用 rsync 进行同步。 改造方案&#xff1a;使用 Go 重写这个抓包逻辑及同步逻辑&#xff0c;直接将抓到的包通过网…

大势智慧亮相“第十届博博会”,展现数字文旅新质生产力!

2024年8月23日至26日&#xff0c;由国家文物局指导&#xff0c;中国博物馆协会主办的第十届“中国博物馆及相关产品与技术博览会”&#xff08;简称“博博会”&#xff09;在呼和浩特敕勒川国际会展中心成功举办。 呼和浩特敕勒川国际会展中心 大势智慧携数字文旅全栈解决方案以…

SpringBoot框架下的客户管理策略

1 绪论 1.1研究背景 随着网络不断的普及发展&#xff0c;企业客户管理系统依靠网络技术的支持得到了快速的发展&#xff0c;首先要从员工的实际需求出发&#xff0c;通过了解员工的需求开发出具有针对性的首页、个人中心、员工管理、客户信息管理、行业类型管理、项目信息管理、…

新能源汽车充电桩怎么选?

新能源汽车是我国七大战略性新兴产业之一&#xff0c;已成为汽车产业转型升级的重要推动力。毫无疑问。充电桩作为我国新能源汽车产业链下游的重要环节&#xff0c;在国家政策的大力支持和市场需求的带动下&#xff0c;有着非常广阔的前景。安科瑞叶西平187-06160015 新能源汽…

数据结构---顺序表之单链表

1.链表的概念 链表是一种逻辑上是线性的&#xff0c;但物理结构不一定是线性的数据结构&#xff0c;它通过链表中的指针链接次序实现的 链表的存储空间是我们通过动态内存开辟的内存空间&#xff0c;所以他们的地址可能是连续的也可能不是连续的 2.链表的分类 1.单向或者双向…

Footprint Analytics: 我们为何打造 Growthly 这款产品

在 Web3 的领域内&#xff0c;数据已成为新的“财富”。在 Footprint Analytics&#xff0c;我们始终站在区块链数据分析的最前沿&#xff0c;提供全方位的解决方案&#xff0c;为 Web3 生态中的企业和项目简化数据分析的复杂性。然而&#xff0c;随着我们对客户需求及行业趋势…

VLAN原理与接口

在学习之前&#xff0c;先抛出问题&#xff1a;什么是VLAN&#xff1f;VLAN工作原理是什么&#xff1f;VLAN如何配置&#xff1f; VLAN的定义 VLAN&#xff08;Virtual Local Area Network&#xff0c;虚拟局域网&#xff09;是一种在逻辑上将局域网设备从物理上划分的网络技…

一种单目标A*算法设计与实现

一种单目标A*算法设计与实现 作者&#xff1a;吴屏珊 最近在学习简单的单目标A*算法&#xff0c;其中在CSDN上阅读到的一篇博文给了我很大启发&#xff0c;于是在该博文的基础上&#xff0c;笔者记录了一点自己对于A*算法的体会和感悟。原文链接 目录 文章目录 一种单目标A*…

微信接口报错:Http头Authorization中的timestamp与 发起请求的时间不得超过5分钟

在发起请求的时候微信会自己封装一个系统时间 当服务器系统时间与实际实际相差5分钟或者5分钟以上就会出现这个错误 需要修改系统时间 问一下ChatGPT 用自动同步 sudo timedatectl set-ntp true 服务器执行 &#xff0c; 过个一分钟 再查看时间 发现正确 再支付就可以成功…