深入解析 @DataScope 数据权限注解


RuoYi 框架实战笔记:深入解析 @DataScope 数据权限注解

1. 前言:为什么需要 @DataScope?

在企业级后台管理系统中,权限控制通常分为两个维度:

  • 功能权限 :控制用户能看到哪些菜单、点击哪些按钮(通常通过 RBAC 模型 + @PreAuthorize 实现)。
  • 数据权限 :控制用户能看到哪几行数据。例如:销售总监能看全公司的订单,部门经理只能看本部门的订单,普通销售只能看自己的订单。

如果要在业务代码中通过 if (user.isManager()) { ... } 这种方式手动拼接 SQL,代码将变得极其臃肿且难以维护。RuoYi 框架提供的 @DataScope 注解,正是利用 AOP(面向切面编程) 技术,优雅地解决了这个问题。


2. 核心原理

@DataScope 的核心思想是:"在 SQL 执行前,根据当前登录用户的角色权限,动态拼接 SQL 过滤条件。"

工作流程

  1. AOP 拦截DataScopeAspect 切面拦截所有标记了 @DataScope 的方法。
  2. 获取角色:获取当前登录用户及其绑定的角色信息。
  3. 生成 SQL :根据角色配置的"数据范围"(Data Scope),生成一段 SQL 片段(如 AND dept_id = 100)。
  4. 参数注入 :将这段 SQL 注入到参数对象(需继承 BaseEntity)的 params 属性中。
  5. MyBatis 执行 :在 XML 中通过 ${params.dataScope} 将过滤条件拼接到主 SQL 后面。

3. 支持的数据权限模式

RuoYi 在"角色管理"中默认支持 5 种数据范围,底层逻辑如下:

权限名称 对应代码 逻辑描述 SQL 示例片段
全部数据权限 DATA_SCOPE_ALL 不进行过滤,可查看所有数据 (无拼接)
自定数据权限 DATA_SCOPE_CUSTOM 根据角色配置的部门列表过滤 OR d.dept_id IN (SELECT dept_id FROM sys_role_dept WHERE role_id = X)
部门数据权限 DATA_SCOPE_DEPT 仅查看本部门数据 OR d.dept_id = 101
部门及以下 DATA_SCOPE_DEPT_AND_CHILD 查看本部门及所有子部门数据 OR d.dept_id IN (SELECT dept_id FROM sys_dept WHERE find_in_set(101, ancestors))
仅本人数据 DATA_SCOPE_SELF 仅查看自己创建的数据 OR u.user_id = 1

4. 使用指南(三步走)

第一步:Service 层添加注解

在需要过滤数据的业务方法上添加注解。

  • deptAlias :指定 SQL 中部门表的别名(默认 d)。
  • userAlias :指定 SQL 中用户表的别名(默认 u)。
java 复制代码
@Service
public class SysUserServiceImpl implements ISysUserService {
    
    @Override
    // 告诉切面:SQL里部门表别名是 d,用户表别名是 u
    @DataScope(deptAlias = "d", userAlias = "u") 
    public List<SysUser> selectUserList(SysUser user) {
        return userMapper.selectUserList(user);
    }
}

第二步:Entity 类继承 BaseEntity

查询参数对象(如 SysUser)必须继承 BaseEntity,因为切面是将生成的 SQL 存入 BaseEntityparams map 中。

java 复制代码
public class SysUser extends BaseEntity {
    // ... 属性定义
}

第三步:Mapper XML 拼接 SQL

在 MyBatis 的 XML 文件中,将生成的 SQL 片段拼接到 WHERE 子句的最后。

注意 :必须使用 $ 符号(文本替换),不能使用 #(预编译)。

xml 复制代码
<select id="selectUserList" parameterType="SysUser" resultMap="SysUserResult">
    SELECT u.user_id, u.dept_id, u.nick_name, d.dept_name
    FROM sys_user u
    LEFT JOIN sys_dept d ON u.dept_id = d.dept_id
    WHERE u.del_flag = '0'
    
    <!-- 关键点:动态拼接数据权限过滤 SQL -->
    ${params.dataScope}
    
</select>

5. 避坑指南与注意事项

  1. 别名必须匹配

    • 注解中的 deptAlias="d" 必须与 XML 中 LEFT JOIN sys_dept d 的别名严格一致。如果 XML 中写的是 LEFT JOIN sys_dept sd,则注解必须改为 deptAlias="sd",否则会报 Unknown column 'd.dept_id' 错误。
  2. 超级管理员特权

    • 如果是超级管理员(admin 或拥有所有权限的角色),切面逻辑通常会直接跳过,不拼接任何 SQL,从而拥有查看所有数据的权限。
  3. 数据表关联

    • 如果要使用部门权限,主查询表必须包含 dept_id 或者关联了 sys_dept 表。
    • 如果要使用"仅本人"权限,主查询表必须包含 user_id 或者关联了 sys_user 表。
  4. 参数对象限制

    • 如果是自定义的 DTO 对象作为查询参数,务必确保它继承了 BaseEntity,或者你自己手动在 DTO 中添加 Map<String, Object> params 字段,否则切面无法注入 SQL。
  5. 多表连接时的歧义

    • 如果多个表都有 dept_id 字段,务必通过 deptAlias 明确指定使用哪张表的字段进行过滤,防止 SQL 字段歧义(Ambiguous column)。

6. 总结

@DataScope 是 RuoYi 框架中解决数据隔离问题的"神兵利器"。它通过 解耦业务代码与权限逻辑,让开发者只需关注 SQL 关联关系,而无需关心复杂的权限判断,极大地提高了开发效率。

相关推荐
架构师专栏16 分钟前
Spring Boot 4 概述与重大变化
spring boot·后端
踏浪无痕16 分钟前
6张表、14步业务逻辑,Mall订单事务凭什么比你的3步事务还稳?
spring boot·spring·面试
解道Jdon44 分钟前
IntelliJ IDEA 2025.3 全面对接 Spring7
spring boot·intellij idea
曾帅1681 小时前
idea springboot开发编译所见即所得应用不需要重启
java·spring boot·intellij-idea
q***01771 小时前
Spring Boot 热部署
java·spring boot·后端
g***72702 小时前
springBoot发布https服务及调用
spring boot·后端·https
想不明白的过度思考者2 小时前
基于 Spring Boot 的 Web 三大核心交互案例精讲
前端·spring boot·后端·交互·javaee
程序员西西3 小时前
SpringCloudGateway入门实战
java·spring boot·计算机·程序员·编程
c***93774 小时前
SpringBoot实现异步调用的方法
java·spring boot·spring