SpringBoot项目使用切面编程实现数据权限管理
springBoot项目使用切面编程实现数据权限管理
- 什么是数据权限管理
- 如何实现数据权限管理
什么是数据权限管理
不同用户在某页面看到数据不一致,实现每个用户之间数据隔离的效果。
如以下场景:
● 页面期望展示当前登录人所在部门的数据。
● 页面期望展示当前登录人所在部门及下级部门的数据。
● 页面期望展示当前登录人创建的数据。
如何实现数据权限管理
接下来我们实现一个简单的数据权限控制,规则只包括自定义sql,目的是在需要的时候将自定义sql拼接到sql中,并将变量替换成对应的值。
1、首先确定用户-角色-菜单-数据权限的关系
菜单有多个数据权限
角色可以绑定多个菜单的,绑定菜单时可以绑定数据权限
用户与角色绑定。
2、定义注解,在方法上使用注解标识当前接口对应的菜单,为了查询数据权限规则,同时该注解可以作为切面的切入点。
/*** 数据权限注解* @Author taoyan* @Date 2019年4月11日*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
@Documented
public @interface PermissionData {/*** 配置菜单的组件路径,用于数据权限*/String permissionId() default "";
}
3、定义数据权限规则信息对象
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
@ApiModel(value = "菜单权限规则表对象", description = "菜单权限规则表")
public class SysPermissionDataRule extends BaseEntity {private static final long serialVersionUID = 1L;@ApiModelProperty(value = "id", required = true)@TableId(value = "id", type = IdType.ASSIGN_ID)private Long id;@ApiModelProperty(value = "对应的菜单id", required = true)@NotNull(message = "对应的菜单id不能为空")private Long permissionId;@ApiModelProperty(value = "规则名称")private String ruleName;@ApiModelProperty(value = "规则值")private String ruleValue;@ApiModelProperty(value = "状态;1:有效,0:无效")private String status;
}
4、定义切面,将用户信息和数据权限规则缓存再request中。
切面:
package com.sinosoft.springbootplus.datapermission.aspect;import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.sinosoft.springbootplus.datapermission.aspect.annotation.PermissionData;
import com.sinosoft.springbootplus.core.context.RequestContext;
import com.sinosoft.springbootplus.datapermission.handler.PermissionDataDepInfoHandler;
import com.sinosoft.springbootplus.datapermission.handler.PermissionDataInfoHandler;
import com.sinosoft.springbootplus.system.domain.entity.SysPermissionDataRule;
import com.sinosoft.springbootplus.system.domain.service.SysPermissionDataRuleDomain;
import com.sinosoft.springbootplus.system.util.DataAutorUtils;
import com.sinosoft.springbootplus.util.HttpServletRequestUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;/*** 数据权限切面处理类* 当被请求的方法有注解PermissionData时,会在往当前request中写入数据权限信息* @Date 2019年4月10日* @Version: 1.0* @author: jeecg-boot*/
@Aspect
@Component
@Slf4j
public class PermissionDataAspect {@Pointcut("@annotation(com.sinosoft.springbootplus.datapermission.aspect.annotation.PermissionData)")public void pointCut() {}@Around("pointCut()")public Object arround(ProceedingJoinPoint point) throws Throwable{HttpServletRequest request = HttpServletRequestUtil.getRequest();//将用户信息放在request请求中MethodSignature signature = (MethodSignature) point.getSignature();Method method = signature.getMethod();PermissionData pd = method.getAnnotation(PermissionData.class);String permissionId = pd.permissionId();//根据userId和菜单id获取绑定的角色对应的数据权限List<SysPermissionDataRule> dataRules = sysPermissionDataRuleDomain.getPermissionDataRulesByCompentAndUserId(permissionId,loginUserId);if(dataRules != null && dataRules.size()>0){//将权限信息暂存在request中DataAutorUtils.installDataSearchConditon(request,dataRules);}//将用户信息存储在request中DataPermissionContext dataPermissionContext = new DataPermissionContext();//从handler中获取用户部门信息if(ObjectUtil.isNotEmpty(permissionDataDepInfoHandler)){Map<String, Object> userInfo = permissionDataDepInfoHandler.getUserInfo(request, loginUserId);dataPermissionContext.putAll(userInfo);}//从handler中获取用户其他信息if (CollUtil.isNotEmpty(permissionDataInfoHandlers)) {for (PermissionDataInfoHandler permissionDataInfoHandler:permissionDataInfoHandlers) {Map<String, Object> userInfo = permissionDataInfoHandler.getUserInfo(request, loginUserId);if (null != userInfo) {dataPermissionContext.putAll(userInfo);}}}DataAutorUtils.installUserInfo(dataPermissionContext);return point.proceed();}
}
切面中往request中放和取用户信息、数据权限的工具类
public class DataAutorUtils {public static final String MENU_DATA_AUTHOR_RULES = "MENU_DATA_AUTHOR_RULES";public static final String SYS_USER_INFO = "SYS_USER_INFO";/*** 往链接请求里面,传入数据查询条件*/public static synchronized void installDataSearchConditon(HttpServletRequest request, List<SysPermissionDataRule> dataRules) {request.setAttribute(MENU_DATA_AUTHOR_RULES, dataRules);}/*** 获取请求对应的数据权限规则*/@SuppressWarnings("unchecked")public static synchronized List<SysPermissionDataRule> loadDataSearchConditon() {return (List<SysPermissionDataRule>) HttpServletRequestUtil.getRequest().getAttribute(MENU_DATA_AUTHOR_RULES);}/*** 将用户信息存到request*/public static synchronized void installUserInfo(DataPermissionContext dataPermissionContext) {HttpServletRequestUtil.getRequest().setAttribute(SYS_USER_INFO, dataPermissionContext);}/*** 从request获取用户信息*/public static synchronized DataPermissionContext loadUserInfo() {return (DataPermissionContext) HttpServletRequestUtil.getRequest().getAttribute(SYS_USER_INFO);}
}
5、在需要拼数据权限的接口上增加注解,并初始化queryWrapper,将request中的参数传进去,包括了该菜单的数据权限和用户信息
4、初始化queryWrapper将数据权限规则拼接到sql中,拼接时会将用户信息map中的信息替换成自定义sql #{ }中的值,因此放用户信息时map中的key与自定义sql中#{ }的值要对应。
/*** 查询生成器*/
@Slf4j
public class QueryGenerator {public static final String SQL_RULES_COLUMN = "SQL_RULES_COLUMN";/*** 获取查询条件构造器QueryWrapper实例 通用查询条件已被封装完成* @param searchObj 查询实体* @param parameterMap request.getParameterMap()* @return QueryWrapper实例*/public static <T> QueryWrapper<T> initQueryWrapper(T searchObj, Map<String, String[]> parameterMap){long start = System.currentTimeMillis();QueryWrapper<T> queryWrapper = new QueryWrapper<T>();installMplus(queryWrapper, searchObj, parameterMap);log.debug("---查询条件构造器初始化完成,耗时:"+(System.currentTimeMillis()-start)+"毫秒----");return queryWrapper;}/*** 组装Mybatis Plus 查询条件* <p>使用此方法 需要有如下几点注意:* <br>1.使用QueryWrapper 而非LambdaQueryWrapper;* <br>2.实例化QueryWrapper时不可将实体传入参数* <br>错误示例:如QueryWrapper<JeecgDemo> queryWrapper = new QueryWrapper<JeecgDemo>(jeecgDemo);* <br>正确示例:QueryWrapper<JeecgDemo> queryWrapper = new QueryWrapper<JeecgDemo>();* <br>3.也可以不使用这个方法直接调用 {@link #initQueryWrapper}直接获取实例*/private static void installMplus(QueryWrapper<?> queryWrapper,Object searchObj,Map<String, String[]> parameterMap) {Map<String,SysPermissionDataRule> ruleMap = getRuleMap();//权限规则自定义SQL表达式for (String c : ruleMap.keySet()) {if(c.startsWith(SQL_RULES_COLUMN)){queryWrapper.and(i ->i.apply(getSqlRuleValue(ruleMap.get(c).getRuleValue())));}}}/*** 获取请求对应的数据权限规则*/public static Map<String, SysPermissionDataRule> getRuleMap() {Map<String, SysPermissionDataRule> ruleMap = new HashMap<>(5);List<SysPermissionDataRule> list = DataAutorUtils.loadDataSearchConditon();if(list != null&&list.size()>0){if(list.get(0)==null){return ruleMap;}for (SysPermissionDataRule rule : list) {String column = SQL_RULES_COLUMN+rule.getId();ruleMap.put(column, rule);}}return ruleMap;}public static String getSqlRuleValue(String sqlRule){try {Set<String> varParams = getSqlRuleParams(sqlRule);for(String var:varParams){String tempValue = converRuleValue(var);sqlRule = sqlRule.replace("#{"+var+"}",tempValue);}} catch (Exception e) {log.error(e.getMessage(), e);}return sqlRule;}/*** 获取sql中的#{key} 这个key组成的set*/public static Set<String> getSqlRuleParams(String sql) {if(StringUtils.isEmpty(sql)){return null;}Set<String> varParams = new HashSet<String>();String regex = "\\#\\{\\w+\\}";Pattern p = Pattern.compile(regex);Matcher m = p.matcher(sql);while(m.find()){String var = m.group();varParams.add(var.substring(var.indexOf("{")+1,var.indexOf("}")));}return varParams;}public static String converRuleValue(String ruleValue) {String value = getUserSystemData(ruleValue);return value!= null ? value : ruleValue;}/*** 从当前用户中获取变量*/public static String getUserSystemData(String key) {DataPermissionContext dataPermissionContext = DataAutorUtils.loadUserInfo();Object o = dataPermissionContext.get(key);if(o instanceof String){return dataPermissionContext.get(key).toString();}if(o instanceof List){List<String> result = new ArrayList<>();for (Object item : (List<?>) o){result.add((String) item);}return "(" + String.join(",", result) + ")";}return null;}}
相关文章:
SpringBoot项目使用切面编程实现数据权限管理
springBoot项目使用切面编程实现数据权限管理什么是数据权限管理如何实现数据权限管理什么是数据权限管理 不同用户在某页面看到数据不一致,实现每个用户之间数据隔离的效果。 如以下场景: ● 页面期望展示当前登录人所在部门的数据。 ● 页面期望展示当…...
亚马逊测评是做什么的,风险有哪些?
自养号测评顾名思义就是自己养国外的买家账号给自己店铺提升销量和评论,做过多年的跨境卖家都知道测评可以快速提高产品的排名、权重和销量,(国内某宝一样的逻辑)但随着测评需求日益增大,卖家在寻求真人测评时也很容易…...
安科瑞导轨式智能通讯管理机
安科瑞 李亚娜 一、概述 AWT200 数据通讯网关应用于各种终端设备的数据采集与数据分析。实现设备的监测、控制、计算,为系统与设备之间建立通讯纽带,实现双向的数据通讯。实时监测并及时发现异常数据,同时自身根据用户规则进行逻辑判断&…...
vs2010下 转换到 COFF 期间失败: 文件无效或损坏
因为同一个电脑上安装多个VS,有多个cvtres.exe。按照下面的操作如果还是不行就在C盘搜索cvtres.exe,然后挨个重命名,看看是调用的哪个,然后修改就可以了。 用VS2010编译C项目时出现这样的错误: LNK1123: 转换到 COFF …...
托福高频真词List19 // 附托福TPO阅读真题
目录 3.28单词 3.29真题 3.28单词 legitimately/properlyadv.正当地likewise/similarlyadv.同样地reveal/showv.揭示substantiate/confirmv.证实suppress/stop by forcev.镇压trend/tendencyn.趋势empirical/based on observationa.凭借经验的illuminate/li…...
Go语言项目标准结构应该如何组织的?
这里写自定义目录标题Go项目本身的目录结构Go语言项目典型目录结构GO语言项目最小标准目录结构可执行的Go语言项目目录结构库的Go语言项目目录结构关于internal目录总结参考文章每当我们写一个非hello world实用程序的Go程序或库时,我们都会在项目结构、代码风格和标…...
设计模式简介
设计模式简介 设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错…...
#详细介绍!!! 线程池的拒绝策略(经典面试题)
本篇单独讲解线程池的拒绝策略,介绍了当线程池任务满了之后,线程池会以什么样的方式来响应添加进来的任务 目录 一:理解线程池拒绝策略的触发情况代码理解 二:线程池的四种常见的拒绝策略 1.ThreadPoolExecutor.AbortPolicy 2…...
正则表达式作业
利用正则表达式完成下面的操作: 一、不定项选择题 能够完全匹配字符串"(010)-62661617"和字符串"01062661617"的正则表达式包括(A ) A. r"\(?\d{3}\)?-?\d{8}" B. r"[0-9()-]" C. r"[0-9(-)]*\d*&qu…...
《扬帆优配》交易拥挤度达历史极值 当前A股TMT板块性价比几何?
上周,A股商场企稳,但盘面风格分歧再度加深:很多资金涌入以ChatGPT、数字经济为代表的TMT板块,而新能源以及前期强势的“中字头”种类都呈现了回调。兴业证券计算显现,3月24日,TMT及电子板块的商场成交金额占…...
C/C++开发,无可避免的IO输入/输出(篇三).字符串流(内存流)IO处理
目录 一、字符串流 1.1 字符串流继承体系 1.2 字符串流本质-类模板std::basic_stringstream 1.3 字符串流缓冲-std::stringbuf 1.4 stringbuf与序列缓冲 1.5 字符串流的打开模式 二、字符串流的运用 2.1 格式转换是其拿手好戏 2.2 字符串流仅提供移动赋值 2.3 std::basic_str…...
什么是HTTP请求?【JavaWeb技术】
HTTP请求是指从客户端到服务器的请求消息,建立HTTP请求需要经历以下7个步骤才能请求成功。 (1)建立TCP连接 在HTTP开始工作前,Web浏览器需先通过网络和Web服务器连接,连接过程主要使用TCP/IP完成。 (2)Web浏览器向Web服务器发送请求命令 一旦…...
浅聊面试这件事
目录 哪个时间点适合跳槽 如何准备面试 面试原则 面试常见问题 哪个时间点适合跳槽 金三银四、金九银十,这些都📌标记为我们的最佳跳槽节点,但是这些节点真的是最佳的么,也需要因人而异。 如果公司年前不发年终奖,…...
【致敬未来的攻城狮计划】连续打卡第7天+瑞萨RA2E1点亮LED
开启攻城狮的成长之旅!这是我参与的由 CSDN博客专家 架构师李肯(http://yyds.recan-li.cn)和 瑞萨MCU (瑞萨电子 (Renesas Electronics Corporation) ) 联合发起的「 致敬未来的攻城狮计划 」的第 7 天,点击…...
Sam Altman专访:GPT-4没太让我惊讶,ChatGPT则让我喜出望外
导读ChatGPT、GPT-4 无疑是 2023 年年初人工智能界最大的「爆款」。3 月 26 日,OpenAI CEO、ChatGPT 之父 Sam Altman 接受了著名学者与科技播客、麻省理工大学研究员 Lex Fridman 的专访,Sam 分享了从OpenAI内部视角如何看待ChatGPT和GPT-4的里程碑式意…...
弯道超车的机会
弯道超车的机会 原文地址:https://bmft.tech/#/1-throught/0302-chance 前言 我一直很想把自己思考的东西表达出来,苦于语文成绩差,文字功力不够,想来想去也不知道用什么话来开场。我不喜欢站在高处对别人指指点点,…...
【设计模式】创建型模式之原型模式
【设计模式】创建型模式之原型模式 文章目录【设计模式】创建型模式之原型模式1.概述2. 构成3. 实现3.1 浅克隆3.2 深克隆1.概述 原型模式(Prototype Pattern):是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它…...
KMP算法——我欲修仙(功法篇)
个人主页:【😊个人主页】 系列专栏:【❤️我欲修仙】 学习名言:莫等闲、白了少年头,空悲切。——岳飞 系列文章目录 第一章 ❤️ 学习前的必知知识 第二章 ❤️ 二分查找 文章目录系列文章目录前言🚗&…...
【嵌入式Linux学习笔记】QT在Linux嵌入式设备上的使用
QT是目前主流的UI界面设计软件之一,Linux系统也支持QT应用,并且提供了很多方便的接口。所以有必要记录一下基于QT,在LCD屏幕上实现UI界面功能的各种细节。 学习视频地址:【正点原子】STM32MP157开发板 1. 系统配置 出于方便&am…...
js根据数据关键字实现模糊查询功能
js根据数据关键字实现模糊查询功能模糊查询实现模糊查询功能的步骤和一般方法第一步:创建假数据或请求接口数据第二步:分析数据格式,处理数据第三步:验证功能完整代码模糊查询 模糊查询功能是指在搜索或者查询时,允许…...
java获取对象属性
Field[] fields vo.getClass().getDeclaredFields(); for (Field field : fields) {//设置允许通过反射访问私有变量field.setAccessible(true);//获取字段的值String value "";Class<?> type field.getType();if (Date.class.equals(type)) {value DateU…...
51单片机(IIC协议OLED屏)
一、IIC协议 1、IIC协议概述 1.1、概述:IIC全称Inter-Integrated Circuit (集成电路总线) 是由PHILIPS公司在80年代开发的两线式串行总线,用于连接微控制器及其外围设备。IIC属于半双 工同步通信方式 1.2、特点:简单性和有效性。 由于接口直…...
你知道,华为对项目经理要求的3项技能5项素质是什么吗?
很多人一定在好奇,华为对项目经理的要求是什么呢?普通项目经理应具备什么素质,才能进入华为这样的大厂,在严峻的经济形势下无惧裁员呢? 一、三项软技能 我们在华为举办的项目经理论坛中找到了答案:对于华…...
优漫动游 提升效率常用的C4D技巧
C4D是近几年非常热的趋势,经常有人问3D相关的问题,想把自己在找捷径的过程中觉得最实用的小技巧分享给大家 1、快速定位层级和模型 模型的过程中,经常遇到模型层级多难定位的问题,逐级打开或者全部展开对于定位模型使…...
基于蚁群算法的时间窗口路径优化
目录 背影 蚁群算法的原理及步骤 基本定义 编程思路 适应度函数 算法的规则 特点 主要参数 代码 结果分析 展望 背影 现代物流配送对时间要求更高,是否及时配送是配送是否成功的重要指标,本文对路径优化加时间窗口,实现基于蚁群算法的时间窗口路径优化, 蚁群算法 基本…...
liunx
linux常用命令 mkdir :创建文件夹 rm -f :删除文件 docker cp 文件名 20f:容器内地址 将文件从linux系统移动到docker地址 ln -s 将两个文件做链接 compgen -u 查看所有用户 groups 查看所在组 vim 编辑 quit 退出 sudo su - root 获得root权限 cp dir1/…...
机动车发票组件【vue】
发票组件 问题反馈:在这就可以 Install-下载 npm install motorvehicles --savewarrning:我们推荐您设置key的,因为不存在它会带来数据的复用性问题usage-使用说明 import MotorVehiclesIvoice from motorvehiclesimport MotorVehiclesIvo…...
学习笔记-剖析k8s之StatefulSet的拓扑状态-3月day18
文章目录前言StatefulSetHeadless ServicePod的拓扑状态小结附前言 Deployment实际上并不足以覆盖所有的应用编排问题,原因在于Deployment对应用做了一个简单化的假设:一个应用的所有Pod,是完全一样的。所以,它们互相之间没有顺序…...
Java实现输出九九乘法口诀表,输入行数输出对应的梯形(平行四边形)这两个代码
目录 一、前言 二、代码部分 1.输出九九乘法口诀表的代码 三、程序运行结果(控制台输出) 一、前言 1.本代码是我在上学时写的,有一些地方没能完美实现,请包涵也请多赐教! 2.本弹窗界面可以根据简单的要求进行输…...
C++空间配置器
目录 1.什么是空间配置器 2.为什么需要空间配置器 3.SGI-STL空间配置器实现原理 3.1一级空间配置器 3.2二级空间配置器 3.2.1内存池 3.2.2 SGI-STL中二级空间配置器设计 3.3 空间配置器的默认选择 4.空间配置器与容器的结合 1.什么是空间配置器 空间配置器࿰…...
邢台移动网站建设价格/百度在线下载
malloc和freemallocfreefree的作用malloc 指在堆区开辟空间给用户使用,而这个区域最大是取决于内存大小 free free(void* p)的参数只有一个,把用malloc开辟的空间销毁掉,而free仅仅传入的是一个首地址,那么free是怎么确定mallo…...
万网站建设/网站外链购买平台
第 1 章:Web基础技术作者:党海峰,孙霞 来源:希赛网 2014年03月14日 HTTP请求消息 HTTP请求消息由Request-Line(请求行)、Header Field(头域)和Message-Body(消息体…...
网站空间租赁 香港/南宁网站seo大概多少钱
山西大同大学数学与计算机科学学院是于2006年在山西大同大学组建后成立的,前身是雁北师范学院数学系。学校坐落于历史文化名城、煤海之乡山西大同。校园占地面积2292.82亩,建筑面积612605.43平方米,中外文藏书180.19万余册,中外文…...
湘潭企业关键词优化厂家报价/青岛网站关键词优化公司
在做一个小东西的时候出现了这个问题,就是使用VS调试几次项目后,使用SQL Server Management Studio管理数据库时,使用SA登录就会出现这个错误,当然,如果项目中的数据库连接字符串中使用的sa验证,那么项目也会连不到数据库的.可是如果是在 Server Management Studio和项目中使用…...
wordpress菠菜插件/推广网
新建一个工程名为“Shapes-Procedural2”,为上篇代码增加描述三角形。修改代码如下: // // main.m // Shapes-Procedural // // Created by cloud on 13-3-7. // Copyright (c) 2013年 cloud. All rights reserved. //#import <Foundation/Founda…...
我的wordpress网站吗/百度信息流平台
上次已经搭建好了基本的环境了,这次我们稍微再配置下,稍微进阶一下。第一步,我们得把我们sql文件导入到我们的mariadb中,也就是导入到mysql数据库中。mysql -u root -p输入密码进入到数据库中,创建数据库,c…...