学习ruoyi权限管理框架的核心

我觉得权限管理是这个系统最主要,最核心的功能,其他的功能只是一些修饰,那如何完成数据权限的管理?下面来看看ruoyi是如何解决的,首先得先了解什么是RBAC模型(基于角色访问控制模型),如果有不明白的可以先学习一下。

这是ruoyi的数据库表划分,我用一个紫色的矩形表示了他是怎么通过在user和权限之间加入一层role来进行灵活的控制。

比如你的某个role前端访问的哪些菜单栏,那么就通过sys_menu和sys_role进行控制。部门同理,这个项目多半是基于部门来进行权限的划分,因为某个员工必属于一个部门

图片.png

首先这个系统的权限分为五种:

全部数据权限:不进行数据过滤

自定义数据权限

本部门数据权限

本部门及以下数据权限

仅本人数据权限

自定义数据权限

主要来分析一下自定义数据权限,他是基于一张sys_role_dept表来映射角色id和部门id的,所以之前说大部分是通过部门来进行权限划分的,可以在前端界面的角色管理里进行权限分配。

图片.png

图片.png

图片.png

核心代码是使用Spring的AOP来实现的,通过自定义注解@DataScope,注解具体功能实现在DataScopeAspect。

if (DATA_SCOPE_CUSTOM.equals(dataScope))
{
    sqlString.append(StringUtils.format(
            " OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ",
             deptAlias,role.getRoleId()));
}

可以看到他是通过我们当前登录用户的roleId来进行sql拼接的(可能会有多个role角色,所以这个代码在循环里面,下面例子中仅使用一个role)。

    select u.user_id, u.dept_id, u.nick_name, u.user_name, u.email, u.avatar, u.phonenumber, u.password, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark, d.dept_name, d.leader
    from sys_user u
    left join sys_dept d on u.dept_id = d.dept_id
    where u.del_flag = '0'
    <!-- 省略一些if判断 -->
    
    <!-- 数据范围过滤 -->
    ${params.dataScope}

有了之前的AOP拼接的SQL,在使用mybatis的插值表达式插入到已有SQL中完成数据过滤。

比如这个用户查询,当前登录的用户角色是2,没有过滤前所有用户都能查询出来,然后加入了拼接的SQL后,仅仅返回用户的部门id在100,101,105,110之中的。

完整的SQL如下

select u.user_id, u.dept_id, u.nick_name, u.user_name, u.email, u.avatar, u.phonenumber, u.password, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark, d.dept_name, d.leader
from sys_user u
left join sys_dept d on u.dept_id = d.dept_id
where u.del_flag = '0'

<!-- 数据范围过滤 -->
AND u.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = 2 )

这样就完成了对用户查询的数据权限过滤。这是调用的代码。

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

除了这个调用,还能找到下列这些数据过滤,可以看到这些service方法调用对应的mapper文件都在SQL中使用了dept_id。可见是用了部门来进行数据权限的管理。

如果要我们在自己的系统中进行数据权限,也需要这么一个能让role标识的id。
图片.png

其他的权限

public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String deptAlias, String userAlias)
{
    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 = {} ) ", deptAlias,
                    role.getRoleId()));
        }
        else if (DATA_SCOPE_DEPT.equals(dataScope))
        {
            sqlString.append(StringUtils.format(" OR {}.dept_id = {} ", deptAlias, user.getDeptId()));
        }
        else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope))
        {
            sqlString.append(StringUtils.format(
                    " OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )",
                    deptAlias, user.getDeptId(), user.getDeptId()));
        }
        else if (DATA_SCOPE_SELF.equals(dataScope))
        {
            if (StringUtils.isNotBlank(userAlias))
            {
                sqlString.append(StringUtils.format(" OR {}.user_id = {} ", userAlias, user.getUserId()));
            }
            else
            {
                // 数据权限为仅本人且没有userAlias别名不查询任何数据
                sqlString.append(" OR 1=0 ");
            }
        }
    }
部门数据权限
StringUtils.format(" OR {}.dept_id = {} ", deptAlias, user.getDeptId())

只能查看同一部门的,A用户在id=1部门,B用户在id=1部门,那A只能查询到自己和B

部门及以下数据权限
StringUtils.format(
" OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )"
,deptAlias, user.getDeptId(), user.getDeptId())

这里使用了一个字段ancestors,他是sys_dept表中的,表示上级所有的部门

图片.png
那么你查询部门105,会将自己和110,11部门查询出来(find_in_set内置函数)。

自身数据权限
if (StringUtils.isNotBlank(userAlias))
 {
sqlString.append(StringUtils.format(" OR {}.user_id = {} ", userAlias, user.getUserId()));
 }
else
{
// 数据权限为仅本人且没有userAlias别名不查询任何数据
sqlString.append(" OR 1=0 ");
}

图片.png
这里加个if判断主要是看自己的业务逻辑,比如在SysDeptServiceImpl和SysRoleSerivceImp中的方法就没有添加userAlias,当这个用户为仅自身数据权限且查看部门信息时,返回空数据集合。

图片.png

总结

总的来说,实现数据权限管理,ruoyi还是做得不错的,可以直接当个脚手架使用。
在访问需要进行数据过滤的数据时,我们对部门进行拼接sql,仅仅查询出符合条件dept_id的数据。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值