ruoyi若依框架@DataScope注解使用以及碰到的一些问题

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS PostgreSQL,高可用系列 2核4GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: ruoyi若依框架@DataScope注解使用以及碰到的一些问题

1️⃣、项目使用场景:

根据不同角色的登录用户,查看不同的列表数据

2️⃣、思路

因为使用的是ruoyi若依框架,所以第一个想到的就是使用@DataScope注解,用来控制数据权限的

3️⃣、具体使用方法

使用起来很简单:

1、在你的实现类的方法上加上该注解 @DataScope(deptAlias = “d”)

@Override
    @DataScope(deptAlias = "d")
    public List<BackupQueueManage> selectBackupQueueManageList(BackupQueueManage backupQueueManage) {
        List<BackupQueueManage> backupQueueManages = backupQueueManageMapper.selectBackupQueueManageList(backupQueueManage);
        for (BackupQueueManage manage : backupQueueManages) {
            String linkScriptId = manage.getLinkScriptId();
            if(StringUtils.isBlank(linkScriptId)){
                linkScriptId = configService.selectConfigByKey(DB2_BACKUP_SCRIPT);
            }
            ......
        }
        return backupQueueManages;
    }

2、在mybatis的xml文件中加上 ${params.dataScope}

<sql id="selectBackupQueueManageVo">
        select queue_id, queue_name from backup_queue_manage d
    </sql>
<
select id="selectBackupQueueManageList" parameterType="BackupQueueManage" resultMap="BackupQueueManageResult">
        <include refid="selectBackupQueueManageVo"/>
        <where>  
            <if test="queueName != null  and queueName != ''"> and queue_name like concat('%', #{queueName}, '%')</if>
            <if test="scheduleType != null  and scheduleType != ''"> and schedule_type = #{scheduleType}</if>
            <if test="executorTime != null  and executorTime != ''"> and executor_time = #{executorTime}</if>
            <if test="dbIds != null  and dbIds != ''"> and db_ids = #{dbIds}</if>
            <if test="jobId != null "> and job_id = #{jobId}</if>
            <if test="isOpen != null  and isOpen != ''"> and is_open = #{isOpen}</if>
            <if test="dbType != null  and dbType != ''"> and db_type = #{dbType}</if>
            <!-- 数据范围过滤 -->
            ${params.dataScope}
        </where>
    </select>

4️⃣、遇到的问题

问题1、我们访问页面的时候,发现如下报错信息

Error querying database. Cause: java.sql.SQLSyntaxErrorException: Unknown column 'd.dept_id' in 'IN/ALL/ANY subquery' 
The error may exist in URL [jar:file:/opt/hidata/hidbm-vue/paas.jar!/BOOT-INF/classes!/mapper/BackupQueueManageMapper.xml] ### 
The error may involve com.hidata.devops.selfops.mapper.BackupQueueManageMapper.selectBackupQueueManageList-Inline ### The error occurred while setting parameters 
SQL: SELECT count(0) FROM backup_queue_manage WHERE (d.dept_id IN (SELECT dept_id FROM sys_dept WHERE dept_id = 100 OR find_in_set(NULL, ancestors)))
 ### Cause: java.sql.SQLSyntaxErrorException: Unknown column 'd.dept_id' in 'IN/ALL/ANY subquery' ; bad SQL grammar []; 
nested exception is java.sql.SQLSyntaxErrorException: Unknown column 'd.dept_id' in 'IN/ALL/ANY subquery'

原因是因为:我们的表中没有加dept_id字段

问题2、报错信息如下:

Error querying database. Cause: java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; 
check the manual that corresponds to your MySQL server version for the right syntax to use near 'IN 
( SELECT dept_id FROM sys_dept WHERE dept_id = 101 or find_in_set( 101 , ance' at line 4  The error may exist in URL
 [jar:file:/opt/hidata/hidbm-vue/paas.jar!/BOOT-INF/classes!/mapper/BackupQueueManageMapper.xml]  The error may involve
 com.hidata.devops.selfops.mapper.BackupQueueManageMapper.selectBackupQueueManageList-Inline  The error occurred while 
 setting parameters  SQL: select count(0) from ( select queue_id, queue_name, schedule_type, executor_time, db_ids, job_id, create_time, is_open,link_script_id
 ,db_type, create_by, update_time, update_by from backup_queue_manage WHERE (.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = 101 or find_in_set( 101 , ancestors ) )) ) tmp_count  Cause: java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'IN ( SELECT dept_id FROM sys_dept WHERE dept_id = 101 or find_in_set( 101 , ance' at line 4 ; bad SQL grammar []; nested exception is java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for 
the right syntax to use near 'IN ( SELECT dept_id FROM sys_dept WHERE dept_id = 101 or find_in_set( 101 , ance' at line 4

通过报错信息,我们发现这么一句 WHERE (.dept_id ,说明sql语句中没有加别名,只要加上即可:select queue_id, queue_name from backup_queue_manage d

5️⃣、若依前台配置用户对应的角色权限

(1)打开角色管理菜单,创建角色,选择该角色可以查看的菜单权限

(2)、分配该角色对应的数据权限

(3)、新增用户,并关联你的角色

6️⃣、若依@DataScope注解底层代码实现

(1)mysql表sys_role中,有一个data_scope字段,该字段为角色对应的数据权限标识

(2)dataScope表示的含义

/**
     * 全部数据权限
     */
    public static final String DATA_SCOPE_ALL = "1";
    /**
     * 自定数据权限
     */
    public static final String DATA_SCOPE_CUSTOM = "2";
    /**
     * 部门数据权限
     */
    public static final String DATA_SCOPE_DEPT = "3";
    /**
     * 部门及以下数据权限
     */
    public static final String DATA_SCOPE_DEPT_AND_CHILD = "4";

(3)后台根据登录用户角色的dataScope动态的拼接sql语句,来进行数据范围的过滤

public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String alias)
    {
        StringBuilder sqlString = new StringBuilder();
        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))
            {
                sqlString.append(StringUtils.format(
                        " OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", alias,
                        role.getRoleId()));
            }
            else if (DATA_SCOPE_DEPT.equals(dataScope))
            {
                sqlString.append(StringUtils.format(" OR {}.dept_id = {} ", alias, user.getDeptId()));
            }
            else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope))
            {
                String deptChild = user.getDept().getParentId() + "," + user.getDeptId();
                sqlString.append(StringUtils.format(
                        " OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or ancestors LIKE '%{}%' )",
                        alias, user.getDeptId(), deptChild));
            }
        }
        if (StringUtils.isNotBlank(sqlString.toString()))
        {
            BaseEntity baseEntity = (BaseEntity) joinPoint.getArgs()[0];
            baseEntity.getParams().put(DATA_SCOPE, " AND (" + sqlString.substring(4) + ")");
        }
    }

(4)、整体逻辑,是采用面向切面编程的思想(AOP),通过注解式开发,来达到数据过滤的作用,关键核心代码在DataScopeAspect类中,具体如下:

package com.ruoyi.framework.aspectj;
import java.lang.reflect.Method;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import com.ruoyi.common.annotation.DataScope;
import com.ruoyi.common.core.domain.BaseEntity;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.util.ShiroUtils;
import com.ruoyi.system.domain.SysRole;
import com.ruoyi.system.domain.SysUser;
/**
 * 数据过滤处理
 * 
 * @author ruoyi
 */
@Aspect
@Component
public class DataScopeAspect
{
    /**
     * 全部数据权限
     */
    public static final String DATA_SCOPE_ALL = "1";
    /**
     * 自定数据权限
     */
    public static final String DATA_SCOPE_CUSTOM = "2";
    /**
     * 部门数据权限
     */
    public static final String DATA_SCOPE_DEPT = "3";
    /**
     * 部门及以下数据权限
     */
    public static final String DATA_SCOPE_DEPT_AND_CHILD = "4";
    /**
     * 数据权限过滤关键字
     */
    public static final String DATA_SCOPE = "dataScope";
    // 配置织入点
    @Pointcut("@annotation(com.ruoyi.common.annotation.DataScope)")
    public void dataScopePointCut()
    {
    }
    @Before("dataScopePointCut()")
    public void doBefore(JoinPoint point) throws Throwable
    {
        handleDataScope(point);
    }
    protected void handleDataScope(final JoinPoint joinPoint)
    {
        // 获得注解
        DataScope controllerDataScope = getAnnotationLog(joinPoint);
        if (controllerDataScope == null)
        {
            return;
        }
        // 获取当前的用户
        SysUser currentUser = ShiroUtils.getSysUser();
        if (currentUser != null)
        {
            // 如果是超级管理员,则不过滤数据
            if (!currentUser.isAdmin())
            {
                dataScopeFilter(joinPoint, currentUser, controllerDataScope.tableAlias());
            }
        }
    }
    /**
     * 数据范围过滤
     * 
     * @param joinPoint 切点
     * @param user 用户
     * @param alias 别名
     */
    public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String alias)
    {
        StringBuilder sqlString = new StringBuilder();
        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))
            {
                sqlString.append(StringUtils.format(
                        " OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", alias,
                        role.getRoleId()));
            }
            else if (DATA_SCOPE_DEPT.equals(dataScope))
            {
                sqlString.append(StringUtils.format(" OR {}.dept_id = {} ", alias, user.getDeptId()));
            }
            else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope))
            {
                String deptChild = user.getDept().getParentId() + "," + user.getDeptId();
                sqlString.append(StringUtils.format(
                        " OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or ancestors LIKE '%{}%' )",
                        alias, user.getDeptId(), deptChild));
            }
        }
        if (StringUtils.isNotBlank(sqlString.toString()))
        {
            BaseEntity baseEntity = (BaseEntity) joinPoint.getArgs()[0];
            baseEntity.getParams().put(DATA_SCOPE, " AND (" + sqlString.substring(4) + ")");
        }
    }
    /**
     * 是否存在注解,如果存在就获取
     */
    private DataScope getAnnotationLog(JoinPoint joinPoint)
    {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        if (method != null)
        {
            return method.getAnnotation(DataScope.class);
        }
        return null;
    }
}


相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
相关文章
|
SQL XML Java
若依框架 --- 使用数据权限功能
若依框架 --- 使用数据权限功能
1810 0
使用ruoyi-vue控制数据权限
使用ruoyi-vue控制数据权限
1785 0
|
前端开发 数据安全/隐私保护
若依框架---权限控制角色设计
若依框架---权限控制角色设计
3488 0
|
SQL 数据库
若依框架---树状层级部门数据库表
若依框架---树状层级部门数据库表
1435 0
|
算法 前端开发 JavaScript
若依框架---数据转树状层级
若依框架---数据转树状层级
979 0
若依框架 --- echarts 封装
若依框架 --- echarts 封装
684 0
|
SQL 监控 NoSQL
架构师第一课,一文带你玩转 ruoyi 架构
我理解的架构/框架应该有以下功能: 1.满足日常开发功能,如单点登陆、消息队列、监控等; 2.规范开发者的开发,指定代码格式、注释等; 3.提高开发效率,提供一系列的封装方法,并减少bug的产生率。 下文将详细介绍ruoyi框架。
8393 1
架构师第一课,一文带你玩转 ruoyi 架构
|
SQL XML JavaScript
【若依Java】15分钟玩转若依二次开发,新手小白半小时实现前后端分离项目,springboot+vue3+Element Plus+vite实现Java项目和管理后台网站功能
摘要: 本文档详细介绍了如何使用若依框架快速搭建一个基于SpringBoot和Vue3的前后端分离的Java管理后台。教程涵盖了技术点、准备工作、启动项目、自动生成代码、数据库配置、菜单管理、代码下载和导入、自定义主题样式、代码生成、启动Vue3项目、修改代码、以及对代码进行自定义和扩展,例如单表和主子表的代码生成、树形表的实现、商品列表和分类列表的改造等。整个过程详细地指导了如何从下载项目到配置数据库,再到生成Java和Vue3代码,最后实现前后端的运行和功能定制。此外,还提供了关于软件安装、环境变量配置和代码自动生成的注意事项。
28312 73
|
11月前
|
监控 NoSQL Java
若依RuoYi项目环境搭建教程(RuoYi-Vue + RuoYi-Vue3版本)
若依(RuoYi)是一款基于Spring Boot和Vue.js的开源Java快速开发脚手架,支持OAuth2、JWT鉴权,集成多种安全框架和持久化框架。它提供了系统管理、监控管理、任务调度、代码生成等常用功能模块,适合中小型公司快速搭建Web应用。本文主要介绍若依框架的特点、版本发展、优缺点及项目部署步骤,帮助开发者快速上手并部署若依项目。
13362 3
若依RuoYi项目环境搭建教程(RuoYi-Vue + RuoYi-Vue3版本)
|
11月前
|
SQL JSON 前端开发
若依RuoYi脚手架二次开发教程(二次开发必学技能)
本次我们将通过一个菜品管理模块开发的案例,来演示拿到若依框架后,如何在若依管理系统上进行二次开发,升级改造为自己的管理系统。适合以若依作为项目脚手架的公司开发人员、毕业设计的学生及开源项目学习者。
6600 1
若依RuoYi脚手架二次开发教程(二次开发必学技能)