一、数据权限实现核心原理

Ruoyi框架通过注解+AOP切面方式实现数据权限控制。核心思想是在SQL执行前动态拼接权限过滤条件,基于用户角色、部门等属性限制数据可见范围。


    二、数据权限作用及设置

    数据权限设置:

    不同角色数据权限不同看到的数据有差别:


    三、数据权限后端源码分析

      (1) 定义数据权限注解

      注解标记@DataScope

      文件:DataScope.java

      数据权限注解@DataScope定义在方法上,包含两个关键属性:

      • deptAlias:部门表别名
      • userAlias:用户表别名
      @Target(ElementType.METHOD)
      @Retention(RetentionPolicy.RUNTIME)
      public @interface DataScope {
          // 部门表的别名(用于SQL拼接)
          String deptAlias() default "";
          // 用户表的别名(用于SQL拼接)
          String userAlias() default "";
      }

      使用示例:

      // SysUserServiceImpl.java 第71行
      @DataScope(deptAlias = "d", userAlias = "u")
      public List<SysUser> selectUserList(SysUser user) {
          return userMapper.selectUserList(user);
      }

      (2) AOP切面拦截(核心逻辑):

        文件:DataScopeAspect.java

        执行时机 在被@DataScope注解的方法执行前拦截

        @Before("@annotation(controllerDataScope)")
        public void doBefore(JoinPoint point, DataScope controllerDataScope) {
            clearDataScope(point);  // 清空防止注入
            handleDataScope(point, controllerDataScope);  // 处理数据权限
        }

        处理步骤(核心方法 dataScopeFilter 的执行流程:):

        1.获取当前登录用户
        LoginUser loginUser = SecurityUtils.getLoginUser();
        SysUser currentUser = loginUser.getUser();
        2.判断是否超级管理员

        超管跳过过滤

        if (!currentUser.isAdmin()) {
            dataScopeFilter(...);
        }
        3.遍历用户角色,拼接SQL条件

        AOP切面DataScopeAspect拦截带有@DataScope注解的方法,通过DataPermissionHelper获取当前用户的权限范围。系统内置五种权限类型:

        1. 全部数据权限(无过滤)
        2. 自定数据权限(自定义SQL)
        3. 部门数据权限(仅本部门)
        4. 部门及以下数据权限(本部门及子部门)
        5. 仅本人数据权限(当前用户)
        for (SysRole role : user.getRoles()) {
            String dataScope = role.getDataScope();
            if (DATA_SCOPE_ALL.equals(dataScope)) {
                // 全部数据权限:清空条件直接跳出
                sqlString = new StringBuilder();
                break;
            }
            else if (DATA_SCOPE_CUSTOM.equals(dataScope)) {
                // 自定义权限:从 sys_role_dept 获取可访问部门
                sqlString.append(" OR d.dept_id IN (SELECT dept_id FROM sys_role_dept WHERE role_id = " + role.getRoleId() + ")");
            }
            else if (DATA_SCOPE_DEPT.equals(dataScope)) {
                // 本部门权限
                sqlString.append(" OR d.dept_id = " + user.getDeptId());
            }
            else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope)) {
                // 本部门及以下权限(利用 ancestors 字段查询子部门)
                sqlString.append(" OR d.dept_id IN (SELECT dept_id FROM sys_dept WHERE dept_id = " + user.getDeptId() + " or find_in_set(" + user.getDeptId() + ", ancestors))");
            }
            else if (DATA_SCOPE_SELF.equals(dataScope)) {
                // 仅本人权限
                sqlString.append(" OR u.user_id = " + user.getUserId());
            }
        }
        4.将SQL条件注入到请求参数
        BaseEntity baseEntity = (BaseEntity) joinPoint.getArgs()[0];
        baseEntity.getParams().put("dataScope", " AND (" + sqlString.substring(4) + ")");

        (3)在Service层使用注解

        文件:SysUserServiceImpl.java 

        @Override
        @DataScope(deptAlias = "d", userAlias = "u")  // ← 触发AOP拦截
        public List<SysUser> selectUserList(SysUser user) {
            return userMapper.selectUserList(user);  // 此时 user.params.dataScope 已注入SQL
        }

        (4)MyBatis动态SQL应用

        在 SysUserMapper.xml 中:通过${params.dataScope}注入动态生成的权限条件

        <select id="selectUserList" parameterType="SysUser" resultMap="SysUserResult">
            SELECT u.user_id, u.user_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'
            <!-- 动态注入数据权限过滤条件 -->
            ${params.dataScope}
        </select>

        最终生成的SQL示例:

        SELECT ... FROM sys_user u LEFT JOIN sys_dept d ON u.dept_id = d.dept_id
        WHERE u.del_flag = '0'
        AND (d.dept_id = 103 OR d.dept_id IN (SELECT dept_id FROM sys_dept WHERE dept_id = 103 or find_in_set(103, ancestors)))

        (5)后端API处理

        处理数据权限配置请求:

        Controller层SysRoleController.java

        @PutMapping("/dataScope")
        public AjaxResult dataScope(@RequestBody SysRole role) {
            roleService.checkRoleAllowed(role);
            roleService.checkRoleDataScope(role.getRoleId());
            return toAjax(roleService.authDataScope(role));
        }

        Service层SysRoleServiceImpl.java

        @Transactional
        public int authDataScope(SysRole role) {
            // 修改角色的 data_scope 字段
            roleMapper.updateRole(role);
            // 删除旧的角色-部门关联
            roleDeptMapper.deleteRoleDeptByRoleId(role.getRoleId());
            // 新增角色和部门信息(仅当选择"自定义数据权限"时)
            return insertRoleDept(role);
        }

        四、自定义数据权限控制

        步骤1.修改Service层,添加数据权限注解:

        在 DeviceInfoServiceImpl 的 selectDeviceInfoList 方法上添加 @DataScope 注解:

        步骤2:修改Mapper XML,添加数据权限过滤条件:
         

        在 DeviceInfoMapper.xml 的查询SQL中添加数据权限过滤条件:

        步骤3:添加表别名

        运行后出现小错误

        错误 SQL SELECT count(0) FROM device_info WHERE (d.dept_id = 105)

        说明:

        SQL 查询中使用了别名 d(如 d.dept_id)

        但在 FROM 子句中 并未给 device_info 表定义别名 d。
        因此数据库无法识别 d.dept_id,因为 d 这个别名根本不存在。

        若以前端操作

        查看实现效果

        Logo

        DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

        更多推荐