当前位置: 首页 > news >正文

RuoYi-Cloud-Plus 数据权限实现原理解析

RuoYi-Cloud-Plus 数据权限实现原理解析

什么是数据权限?

数据权限是控制用户能够访问哪些数据的权限机制。在实际业务场景中,我们经常遇到这样的需求:

  • 普通员工只能查看自己创建的数据
  • 部门经理可以查看本部门所有员工的数据
  • 总经理可以查看全公司的数据

这种按照用户角色和组织结构控制数据访问范围的机制就是数据权限控制。

RuoYi-Cloud-Plus 数据权限设计思路

RuoYi-Cloud-Plus 采用了 AOP(面向切面编程)+ MyBatis 拦截器的组合方式来实现数据权限控制:

  1. 通过自定义注解标记需要进行数据权限控制的方法
  2. 利用 AOP 在方法执行前设置权限上下文
  3. 通过 MyBatis 拦截器拦截 SQL 语句并动态添加过滤条件

核心组件解析

1. 数据权限注解

RuoYi-Cloud-Plus 定义了两个核心注解:

@DataPermission 注解

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataPermission {DataColumn[] value();     // 数据权限配置数组String joinStr() default ""; // 权限拼接标识符
}

@DataPermission 注解用于标记需要进行数据权限控制的方法或类,其中:

  • value:包含一个或多个 @DataColumn 注解,定义数据权限的详细配置
  • joinStr:指定多个数据权限条件之间的连接方式(AND 或 OR)

@DataColumn 注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataColumn {String[] key() default "deptName";    // 占位符关键字String[] value() default "dept_id";   // 占位符替换值String permission() default "";       // 权限标识符
}

@DataColumn 注解用于定义数据权限的占位符和替换值:

  • key:SQL 模板中的占位符关键字
  • value:实际数据库表中的字段名
  • permission:权限标识符,拥有此权限的用户将不受数据权限限制

2. AOP 切面处理

AOP 切面由三个核心组件构成:

DataPermissionPointcut - 切点定义

public class DataPermissionPointcut extends StaticMethodMatcherPointcut {@Overridepublic boolean matches(Method method, Class<?> targetClass) {// 匹配带有 @DataPermission 注解的方法if (method.isAnnotationPresent(DataPermission.class)) {return true;}// 处理 JDK 动态代理的情况Class<?> targetClassRef = targetClass;if (Proxy.isProxyClass(targetClassRef)) {targetClassRef = targetClass.getInterfaces()[0];}return targetClassRef.isAnnotationPresent(DataPermission.class);}
}

DataPermissionAdvice - 通知逻辑

public class DataPermissionAdvice implements MethodInterceptor {@Overridepublic Object invoke(MethodInvocation invocation) throws Throwable {Object target = invocation.getThis();Method method = invocation.getMethod();Object[] args = invocation.getArguments();// 设置权限注解到上下文DataPermissionHelper.setPermission(getDataPermissionAnnotation(target, method, args));try {// 执行目标方法return invocation.proceed();} finally {// 清除权限注解DataPermissionHelper.removePermission();}}
}

DataPermissionPointcutAdvisor - 切面组合

public class DataPermissionPointcutAdvisor extends AbstractPointcutAdvisor {private final Advice advice;private final Pointcut pointcut;public DataPermissionPointcutAdvisor() {this.advice = new DataPermissionAdvice();this.pointcut = new DataPermissionPointcut();}@Overridepublic Pointcut getPointcut() {return this.pointcut;}@Overridepublic Advice getAdvice() {return this.advice;}
}

3. 权限上下文管理

DataPermissionHelper 负责管理权限上下文,使用 ThreadLocal 存储当前线程的权限信息:

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class DataPermissionHelper {// 使用 ThreadLocal 存储当前线程的权限注解private static final ThreadLocal<DataPermission> PERMISSION_CACHE = new ThreadLocal<>();// 设置权限注解public static void setPermission(DataPermission dataPermission) {PERMISSION_CACHE.set(dataPermission);}// 获取权限注解public static DataPermission getPermission() {return PERMISSION_CACHE.get();}// 清除权限注解public static void removePermission() {PERMISSION_CACHE.remove();}
}

4. MyBatis 拦截器

MyBatis 拦截器负责在 SQL 执行前动态添加数据权限过滤条件:

public class PlusDataPermissionInterceptor extends BaseMultiTableInnerInterceptor {private final PlusDataPermissionHandler dataPermissionHandler = new PlusDataPermissionHandler();@Overridepublic void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {// 检查是否需要忽略数据权限处理if (InterceptorIgnoreHelper.willIgnoreDataPermission(ms.getId())) {return;}// 检查是否缺少有效的数据权限注解if (dataPermissionHandler.invalid()) {return;}// 解析并修改 SQLPluginUtils.MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql);mpBs.sql(parserSingle(mpBs.sql(), ms.getId()));}
}

数据权限处理流程

1. 方法调用阶段

当执行带有 @DataPermission 注解的方法时:

  1. AOP 拦截器捕获方法调用
  2. 从方法或类上获取 @DataPermission 注解信息
  3. 将注解信息存储到 ThreadLocal
  4. 执行目标方法(通常是 MyBatis Mapper 方法)
  5. 方法执行完成后,清除 ThreadLocal 中的注解信息

2. SQL 拦截阶段

当 MyBatis 执行 SQL 语句时:

  1. MyBatis 拦截器捕获 SQL 执行请求
  2. ThreadLocal 中获取权限注解信息
  3. 根据当前用户角色和权限范围构建过滤条件
  4. 将过滤条件动态添加到 SQL 中

3. 权限条件构建

权限条件构建过程如下:

  1. 获取当前登录用户信息
  2. 判断用户是否为超级管理员(超级管理员不受数据权限限制)
  3. 根据用户角色获取数据范围类型
  4. 使用 SpEL 表达式解析权限模板
  5. 替换占位符生成最终的 SQL 过滤条件

实际使用示例

基本使用

// 在 Mapper 接口上使用数据权限注解
@DataPermission({@DataColumn(key = "deptName", value = "dept_id"),@DataColumn(key = "userName", value = "user_id")
})
public interface SysUserMapper {List<SysUser> selectUserList(SysUser user);
}

假设当前用户是部门经理,属于部门 ID 为 1、2、3 的部门,用户 ID 为 1001,生成的 SQL 可能类似于:

SELECT * FROM sys_user WHERE (dept_id IN (1, 2, 3) OR user_id = 1001)

使用 joinStr 控制连接方式

@DataPermission(value = {@DataColumn(key = "deptName", value = "dept_id"),@DataColumn(key = "userName", value = "user_id")
}, joinStr = "AND")
public interface SysUserMapper {List<SysUser> selectUserList(SysUser user);
}

使用 joinStr = "AND" 后,生成的 SQL 可能类似于:

SELECT * FROM sys_user WHERE (dept_id IN (1, 2, 3) AND user_id = 1001)

使用权限标识符

@DataPermission({@DataColumn(key = "deptName", value = "dept_id"),@DataColumn(key = "userName", value = "user_id", permission = "system:user:all")
})
public interface SysUserMapper {List<SysUser> selectUserList(SysUser user);
}

如果用户拥有 "system:user:all" 权限,则不受数据权限限制,生成的 SQL 不会添加额外的过滤条件。

joinStr 参数详解

在 @DataPermission 注解中,joinStr 属性用于指定多个数据权限条件之间的连接方式:

  • 不指定 joinStr:查询操作默认使用 "OR",更新/删除操作默认使用 "AND"
  • 指定 joinStr = "AND":多个条件使用 AND 连接
  • 指定 joinStr = "OR":多个条件使用 OR 连接

总结

RuoYi-Cloud-Plus 的数据权限实现具有以下优势:

  1. 无侵入性:通过注解方式实现,业务代码无需修改
  2. 灵活性:支持在方法和类级别使用,可以定义多个数据列的过滤规则
  3. 可扩展性:通过 SpEL 表达式支持复杂的权限逻辑
  4. 高性能:使用 ThreadLocal 存储上下文信息,避免重复查询
http://www.hskmm.com/?act=detail&tid=39060

相关文章:

  • 第5天(中等题 滑动窗口、逆向思维)
  • P10老板一句‘搞不定就P0’,15分钟我用Arthas捞回1000万资损 - 指南
  • 华为堡垒机
  • [HZOI] CSP-S模拟38 赛后总结
  • Meet in the middle 学习笔记
  • 集合常见操作示例
  • 深入解析:港大和字节携手打造WorldWeaver:以统一建模方案整合感知条件,为长视频生成领域带来质量与一致性双重飞跃。
  • 集合与列表有何不同的使用场景,如何选择?
  • 虚拟机下 安装 ubuntu 18.04
  • MinIO快速入门
  • 多表查询-练习
  • 实验3:卷积神经网络 - OUC
  • 使用 Docker Compose 在 CentOS 7 单机服务器上部署多实例 MinIO 集群
  • 102302147傅乐宜作业1
  • 多智能体大模型在农业中的应用研究与展望
  • 嵌入式基础作业--第七周--IIC协议采集温湿度与OLED显示
  • Nature子刊 | 基于生物学信息的神经网络
  • 软件开发(10.23)
  • 2025年项目总延期?这30款项目进度管理软件一定有一款适合你!
  • Educational Codeforces Round 66 (Rated for Div. 2) A~F
  • 鲁东大学提出可解释的自适应集成机器学习全基因组选择算法用于小麦产量性状关键SNPs筛选
  • 台球厅收银台押金原路退回系统押金预授权—东方仙盟 - 详解
  • if 语句
  • 数论专题小记
  • 机械臂和相机的9点标定原理
  • 遗传改良中的核心技术:交配设计
  • 《程序员修炼之道:从小工到专家》笔记1
  • 语言是火,视觉是光:论两种智能信号的宿命与人机交互的未来 - 教程
  • 书籍推荐 | 《数量遗传学》(王建康)
  • Plant Com | 一种新的多源数据(基因组、表型和跨环境)融合的基因组预测框架-GPS