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

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用系列 2核4GB
简介: 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;
    }
}


相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
7月前
|
XML Java 数据格式
Springboot中自定义组件
Springboot中自定义组件
|
JavaScript Java
springboot和vue项目如何上传图片,结合若依框架实现
springboot和vue项目如何上传图片,结合若依框架实现
483 0
|
29天前
|
XML Java 数据格式
SpringBoot入门(8) - 开发中还有哪些常用注解
SpringBoot入门(8) - 开发中还有哪些常用注解
40 2
|
2月前
|
前端开发 JavaScript Java
【SpringBoot系列】视图解析器的搭建与开发
【SpringBoot系列】视图解析器的搭建与开发
45 0
|
7月前
|
前端开发 Java 数据库连接
【Java实战篇】SpringBoot+MyBatis快速实现登录注册
【1月更文挑战第19天】【Java实战篇】SpringBoot+MyBatis快速实现登录注册
|
6月前
|
Java
手写SpringBoot(四)之bean动态加载
可以看到只有userApplication tomcatWebServer init打印,没有mySpringboot tomcatWebServer init打印,证明spring-boot只加载了用户定义的那个tomcatWebServer
47 0
|
7月前
|
前端开发 JavaScript Java
Java网络商城项目 SpringBoot+SpringCloud+Vue 网络商城(SSM前后端分离项目)五(前端页面
Java网络商城项目 SpringBoot+SpringCloud+Vue 网络商城(SSM前后端分离项目)五(前端页面
Java网络商城项目 SpringBoot+SpringCloud+Vue 网络商城(SSM前后端分离项目)五(前端页面
|
7月前
|
前端开发
基于若依ruoyi-nbcio增加flowable流程是否结束的接口
基于若依ruoyi-nbcio增加flowable流程是否结束的接口
98 1
|
7月前
|
前端开发 JavaScript Java
Springboot框架整合jsp开发【干货满满】
该文介绍了如何在Spring Boot中集成JSP,需包含`spring-boot-starter-web`、`tomcat-embed-jasper`和`jstl`三个依赖。配置Spring Boot寻找JSP的位置,设置`spring.mvc.view.prefix`为`/WEB-INF/jsp/`,`spring.mvc.view.suffix`为`.jsp`。JSP文件应放在`src/main/webapp/WEB-INF/jsp/`下。
107 0
|
7月前
|
JavaScript Java 测试技术
SpringBoot原理分析 | 开源框架:Swagger集成
SpringBoot原理分析 | 开源框架:Swagger集成
80 0