数据权限实现

权限框架可以根据用户所属角色决定有权限看到的菜单资源权限。
同一个资源下的同一个菜单的数据权限需要单独处理。

案例:一部门的张三和二部门的李四都是普通用户角色,普通用户都有用户管理的查询权限,但是,一部门的张三只能看到一部门以及一部门下面的数据的权限。

效果图:
测试部门的test001用户之后能看到自己部门的数据,需求部门的数据看不到
在这里插入图片描述
在这里插入图片描述

文章目录

          • 一、数据权限模型
            • 1. 实现原理
            • 2. 实现流程
            • 3. 权限标识对象
            • 4. 查询SQL拦截器
            • 5. 查询SQL特殊处理
          • 二、数据权限使用
            • 2.1. 控制层
            • 2.2. service
            • 2.3. mapper
            • 2.4. 获取部门集合
            • 2.5.
          • 三、实战演练
            • 3.1. 获取当前用户的所属部门
            • 3.2. 获取当前用户的所属部门以及子部门集合
            • 3.3. 调用逻辑service层执行查询逻辑
            • 3.4. 调用mapper执行查询逻辑
            • 3.5. 在查询数据库之前拦截处理
            • 3.6. 未处理的原sql
            • 3.7. 处理后的sql

一、数据权限模型
1. 实现原理
1.创建一个需要执行数据权限的标志
2.根据数据权限标识对原查询SQL,在查询数据库之前进行特殊处理
3.利用拦截器在使用mapper在查询数据库时进行拦截处理1>获取所有的查询SQL2>获取查询sql中的的数据权限标识无:放行有:动态拼接数据权限sql
2. 实现流程
1.创建一个需要执行数据权限的标志对象DataScope
2.除超级管理员外,进行数据权限处理
3.获取当前用户的所处部门ID,根据部门ID获取当前部门以及子部门的ID集合
4.创建数据范围的拦截器DataScopeInterceptor
5.再判断根据数据权限标识对原查询SQL,利用拦截器在使用mapper在查询数据库时进行拦截处理1>获取所有的查询SQL2>获取查询sql中的的数据权限标识无:放行有:动态拼接数据权限sql简言之:将源sql看做一个查询整体,最后将结果集按照部门ids集合进行模糊区配筛选出(当前部门以及子部门的权限范围)
3. 权限标识对象
package cn.stylefeng.roses.core.datascope;import java.util.List;/*** 数据范围** @author fengshuonan* @date 2017-07-23 22:19*/
public class DataScope {/*** 限制范围的字段名称*/private String scopeName = "deptid";/*** 具体的数据范围*/private List<Long> deptIds;public DataScope() {}public DataScope(List<Long> deptIds) {this.deptIds = deptIds;}public DataScope(String scopeName, List<Long> deptIds) {this.scopeName = scopeName;this.deptIds = deptIds;}public List<Long> getDeptIds() {return deptIds;}public void setDeptIds(List<Long> deptIds) {this.deptIds = deptIds;}public String getScopeName() {return scopeName;}public void setScopeName(String scopeName) {this.scopeName = scopeName;}
}
4. 查询SQL拦截器
5. 查询SQL特殊处理
package cn.stylefeng.roses.core.datascope;import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;import java.sql.Connection;
import java.util.List;
import java.util.Map;
import java.util.Properties;/*** 数据范围的拦截器** @author fengshuonan* @date 2017-07-23 21:26*/
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class DataScopeInterceptor implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {StatementHandler statementHandler = (StatementHandler) PluginUtils.realTarget(invocation.getTarget());MetaObject metaStatementHandler = SystemMetaObject.forObject(statementHandler);MappedStatement mappedStatement = (MappedStatement) metaStatementHandler.getValue("delegate.mappedStatement");if (!SqlCommandType.SELECT.equals(mappedStatement.getSqlCommandType())) {return invocation.proceed();}BoundSql boundSql = (BoundSql) metaStatementHandler.getValue("delegate.boundSql");String originalSql = boundSql.getSql();Object parameterObject = boundSql.getParameterObject();//查找参数中包含DataScope类型的参数DataScope dataScope = findDataScopeObject(parameterObject);if (dataScope == null) {return invocation.proceed();} else {String scopeName = dataScope.getScopeName();List<Long> deptIds = dataScope.getDeptIds();String join = CollectionUtil.join(deptIds, ",");originalSql = "select * from (" + originalSql + ") temp_data_scope where temp_data_scope." + scopeName + " in (" + join + ")";metaStatementHandler.setValue("delegate.boundSql.sql", originalSql);return invocation.proceed();}}/*** 查找参数是否包括DataScope对象*/public DataScope findDataScopeObject(Object parameterObj) {if (parameterObj instanceof DataScope) {return (DataScope) parameterObj;} else if (parameterObj instanceof Map) {for (Object val : ((Map<?, ?>) parameterObj).values()) {if (val instanceof DataScope) {return (DataScope) val;}}}return null;}@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}@Overridepublic void setProperties(Properties properties) {}
}
二、数据权限使用
2.1. 控制层
/*** 查询管理员列表** @author gblfy* @Date 2020/12/24 22:43*/@RequestMapping("/list")@Permission@ResponseBodypublic Object list(@RequestParam(required = false) String name,@RequestParam(required = false) String timeLimit,@RequestParam(required = false) Long deptId) {//拼接查询条件String beginTime = "";String endTime = "";if (ToolUtil.isNotEmpty(timeLimit)) {String[] split = timeLimit.split(" - ");beginTime = split[0];endTime = split[1];}if (ShiroKit.isAdmin()) {Page<Map<String, Object>> users = userService.selectUsers(null, name, beginTime, endTime, deptId);Page wrapped = new UserWrapper(users).wrap();return LayuiPageFactory.createPageInfo(wrapped);} else {DataScope dataScope = new DataScope(ShiroKit.getDeptDataScope());Page<Map<String, Object>> users = userService.selectUsers(dataScope, name, beginTime, endTime, deptId);Page wrapped = new UserWrapper(users).wrap();return LayuiPageFactory.createPageInfo(wrapped);}}
2.2. service
/*** 根据条件查询用户列表** @author gblfy* @Date 2020/12/24 22:45*/public Page<Map<String, Object>> selectUsers(DataScope dataScope, String name, String beginTime, String endTime, Long deptId) {Page page = LayuiPageFactory.defaultPage();return this.baseMapper.selectUsers(page, dataScope, name, beginTime, endTime, deptId);}
2.3. mapper
/*** 根据条件查询用户列表*/Page<Map<String, Object>> selectUsers(@Param("page") Page page, @Param("dataScope") DataScope dataScope, @Param("name") String name, @Param("beginTime") String beginTime, @Param("endTime") String endTime, @Param("deptId") Long deptId);
<select id="selectUsers" resultType="map">select<include refid="Base_Column_List"/>from sys_userwhere status != 'DELETED'<if test="name != null and name != ''">and (phone like CONCAT('%',#{name},'%')or account like CONCAT('%',#{name},'%')or name like CONCAT('%',#{name},'%'))</if><if test="deptId != null and deptId != 0">and (dept_id = #{deptId} or dept_id in ( select dept_id from sys_dept where pids like CONCAT('%$[', #{deptId}, '$]%') escape '$' ))</if><if test="beginTime != null and beginTime != '' and endTime != null and endTime != ''">and (create_time between CONCAT(#{beginTime},' 00:00:00') and CONCAT(#{endTime},' 23:59:59'))</if></select>
2.4. 获取部门集合
    /*** 获取当前用户的部门数据范围的集合*/public static List<Long> getDeptDataScope() {Long deptId = getUser().getDeptId();List<Long> subDeptIds = ConstantFactory.me().getSubDeptId(deptId);subDeptIds.add(deptId);return subDeptIds;}/*** 获取子部门id*/@Overridepublic List<Long> getSubDeptId(Long deptId) {ArrayList<Long> deptIds = new ArrayList<>();if (deptId == null) {return deptIds;} else {List<Dept> depts = this.deptMapper.likePids(deptId);if (depts != null && depts.size() > 0) {for (Dept dept : depts) {deptIds.add(dept.getDeptId());}}return deptIds;}}
2.5.
三、实战演练
3.1. 获取当前用户的所属部门

在这里插入图片描述

3.2. 获取当前用户的所属部门以及子部门集合

在这里插入图片描述
在这里插入图片描述

3.3. 调用逻辑service层执行查询逻辑

在这里插入图片描述

3.4. 调用mapper执行查询逻辑

在这里插入图片描述

3.5. 在查询数据库之前拦截处理

在这里插入图片描述

3.6. 未处理的原sql
selectuser_id AS "userId", avatar AS "avatar", account AS "account", salt AS "salt", name AS "name", birthday AS "birthday", sex AS "sex", email AS "email", phone AS "phone", role_id AS "roleId", dept_id AS "deptId", status AS "status", create_time AS "createTime", create_user AS "createUser", update_time AS "updateTime", update_user AS "updateUser", version AS "version"from sys_userwhere status != 'DELETED'
3.7. 处理后的sql
select * from (selectuser_id AS "userId", avatar AS "avatar", account AS "account", salt AS "salt", name AS "name", birthday AS "birthday", sex AS "sex", email AS "email", phone AS "phone", role_id AS "roleId", dept_id AS "deptId", status AS "status", create_time AS "createTime", create_user AS "createUser", update_time AS "updateTime", update_user AS "updateUser", version AS "version"from sys_userwhere status != 'DELETED') temp_data_scope where temp_data_scope.deptid in (26)

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

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

相关文章

遍地开花的 Attention ,你真的懂吗?

阿里妹导读&#xff1a;曾被 paper 中各种各样的 Attentioin 搞得晕晕乎乎&#xff0c;尽管零零散散地整理过一些关于Attention 的笔记&#xff0c;重点和线索依然比较凌乱。今天&#xff0c;阿里巴巴工程师楠易&#xff0c;将 Attentioin 的知识系统性地梳理、回顾、总结&…

Wrapper+map实现页面显示

文章目录1. 查询用户数据map集合2. map集合参数拼装1. 查询用户数据map集合 2. map集合参数拼装 用户角色和部门名称&#xff0c;根据角色ID和部门id分别查询替换&#xff0c;简言之&#xff1a;需要的内容分别通过单独查询数据库得到&#xff0c;然后通过遍历依次对比&#xf…

手淘促活那些事儿 | 智能投放算法框架助力用户增长

导读&#xff1a;本文主要介绍以手淘促活为目的的全链路智能投放算法框架&#xff0c;该框架目前接入以 Pagani 为核心的全链路运营平台&#xff0c;首先使用用户意图识别算法圈选出目标人群&#xff0c;然后借助物料智能推荐和权益动态面额等算法实现全链路上用户的个性化触达…

天天用Redis,持久化方案你又知道哪些?

来源 |码猿技术专栏责编 | Carol头图 | CSDN 下载自视觉中国Redis目前已经成为主流的内存数据库了&#xff0c;但是大部分人仅仅是停留在会用的阶段&#xff0c;你真的了解Redis内部的工作原理吗&#xff1f;今天这篇文章将为大家介绍Redis持久化的两种方案&#xff0c;文章将会…

万万没想到,JVM内存结构的面试题可以问的这么难?

在我的博客中&#xff0c;之前有很多文章介绍过JVM内存结构&#xff0c;相信很多看多我文章的朋友对这部分知识都有一定的了解了。 那么&#xff0c;请大家尝试着回答一下以下问题&#xff1a; 1、JVM管理的内存结构是怎样的&#xff1f; 2、不同的虚拟机在实现运行时内存的…

Serverless 落地挑战与蚂蚁金服实践

目前 Serverless 已成为云原生社区关注的重点之一&#xff0c;有人说它是微服务的继承者&#xff0c;将会彻底改变软件研发的现状&#xff0c;那么真实情况如何呢&#xff1f;本文将介绍 Serverless 市场观察、落地挑战&#xff0c;以及蚂蚁金服对 Serverless 的实践。 Server…

程序员感叹一年只能存下15万太少了……网友:潸然泪下

最近有程序员网友晒出自己的年终奖&#xff0c;税后高达15.7万&#xff01;看到这个情形&#xff0c;很多网友表示自己“被打鸡血了”。他强调学习的重要性&#xff0c;学习仍然是在这个时代下&#xff0c;普通人能够逆袭&#xff0c;给家人更好生活的一把利器&#xff01;今天…

机器学习工程师第一年的12点体会

机器学习和数据科学都是广义上的术语&#xff0c;它们涉及超级多的领域以及知识&#xff0c;一位数据科学家所做的事情可能与另一位有很大的不同&#xff0c;机器学习工程师也是如此。通常使用过去&#xff08;数据&#xff09;来理解或预测&#xff08;构建模型&#xff09;未…

今天下午三点,2020深圳开放数据应用创新大赛将举行第二场线上推介会

4月27日15:00 - 16:30&#xff0c;网易客户端、ZAKER、华为云、南方 plus、虎牙在线直播每场线上推介会设两轮抽奖&#xff0c;欢迎互动。继4月20日首场线上推介会顺利举行后&#xff0c;4月27日下午&#xff0c;以"数聚粤港澳&#xff0c;智汇大湾区"为主题的"…

解决Another app is currently holding the yum lock; waiting for it to exit...问题

在下载安装lrzsz时出现Another app is currently holding the yum lock; waiting for it to exit...问题yum被锁定了可以使用 rm -rf /var/run/yum.pid 强制杀死进程来解决

优化 Tengine HTTPS 握手时间

背景 网络延迟是网络上的主要性能瓶颈之一。在最坏的情况下&#xff0c;客户端打开一个链接需要DNS查询&#xff08;1个 RTT&#xff09;&#xff0c;TCP握手&#xff08;1个 RTT&#xff09;&#xff0c;TLS 握手&#xff08;2个RTT&#xff09;&#xff0c;以及最后的 HTTP …

揭秘!如何用Flutter设计一个100%准确的埋点框架?

阿里妹导读&#xff1a;用户行为埋点是用来记录用户在操作时的一系列行为&#xff0c;也是业务做判断的核心数据依据&#xff0c;如果缺失或者不准确将会给业务带来不可恢复的损失。闲鱼将业务代码从Native迁移到Flutter上过程中&#xff0c;发现原先Native体系上的埋点方案无法…

安装rzsz

yum install -y lrzsz

抽象思想解读Linux进程描述符

来源 | 嵌入式客栈责编 | Carol头图 | CSDN 下载自视觉中国内核是怎么工作的&#xff1f;首先要理解进程管理&#xff0c;进程调度&#xff0c;本文开始阅读进程管理部分&#xff0c;首先从进程的抽象描述开始。抽象是软件工程的灵魂&#xff0c;而对于Linux操作系统而言&#…

CentOS Linux 7.7 安装kafka zookeeper

文章目录一、软件下载1. zookeeper2. kafka二、安装与启动2.1. jdk2.2. zookeeper2.3. kafka三、 kafka 基本演示一、软件下载 1. zookeeper http://archive.apache.org/dist/zookeeper/zookeeper-3.5.7/apache-zookeeper-3.5.7-bin.tar.gz 2. kafka https://archive.apach…

《Java开发手册》2019最新版发布!

致全球Java开发者&#xff1a; 代码是二进制世界的交流方式&#xff0c;极致的代码是我们的荣耀。 2017年春天&#xff0c;《阿里巴巴Java开发手册》发布&#xff0c;我们希望在涵盖编程规约、异常日志、单元测试、安全规约、MySQL数据库、工程规约、设计规约等7个维度上为开…

IDEA 惊天 bug:进程已结束,退出代码 1073741819

来源 | 沉默王二责编 | Carol头图 | CSDN 下载自视觉中国今天要写的文章中涉及到一串代码&#xff0c;关于 Undertow 的一个入门示例&#xff0c;贴出来大家看一下。public class UndertowTest {public static void main(final String[] args) {Undertow server Undertow.buil…

python3-Anaconda3 基本使用

1、下载 最新版本官网下载&#xff1a; https://www.anaconda.com/distribution/ 历史版本 清华镜像&#xff08;国内首选&#xff09;&#xff1a; https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/ 历史版本 官网镜像&#xff1a; https://repo.anaconda.com/archi…

淘宝应用柔性架构的探索

导读&#xff1a;随着淘宝业务的飞速发展&#xff0c;微服务架构在持续演进的过程中&#xff0c;也受到了越来越多的挑战&#xff1a;如同步模型带来的资源利用率有限、依赖调用并发度有限、下游故障引发应用自身出问题&#xff1b;又如静态限流随着业务代码的演进、依赖拓扑的…

iOS13 一次Crash定位 - 被释放的NSURL.host

每年一次的iOS升级&#xff0c;都会给开发者带来一些适配工作&#xff0c;一些原本工作正常的代码可能就会发生崩溃。 本文讲到了一种 CoreFoundation 对象的内存管理方式在iOS13上遇到的问题。 1. 问题 iOS 13 Beta 版本上&#xff0c;手淘出现了一个必现的崩溃&#xff1a; …