这是我参与11月更文挑战的第10天,活动详情查看:2021最后一次更文挑战
前言
本节介绍RuoYi-Vue的模块中是如何进行数据权限切面的,这里很重要,同时也很危险,因为动态加入sql,往往会带来sql注入的漏洞。因此如果你掌握不好,那最好的办法还是单独写对应的各权限页面而不是像Ruoyi这样进行权限动态添加。
权限字段
Ruoyi是在数据库的sys_role表中添加的data_scope字段,分别限定了数据范围为1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限
对应的注解
在对应的需要控制权限的接口添加,可以传入部门表别名和用户表别名,这样方便不同的sql中可以同步不同的表别名。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataScope
{
/**
* 部门表的别名
*/
public String deptAlias() default "";
/**
* 用户表的别名
*/
public String userAlias() default "";
}
对应的权限控制sql逻辑
首先在处理权限过滤之前需要进行params.dataScope参数防止注入
<!-- 数据范围过滤 -->
${params.dataScope}
这些对应的数据权限的sql,都是用$修饰的,而不是用#修饰的,因此就会带来sql注入的问题。
之后按照用户的权限进行数据权限sql的拼接
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 ");
}
}
}
if (StringUtils.isNotBlank(sqlString.toString())) {
Object params = joinPoint.getArgs()[0];
if (StringUtils.isNotNull(params) && params instanceof BaseEntity) {
BaseEntity baseEntity = (BaseEntity) params;
baseEntity.getParams().put(DATA_SCOPE, " AND (" + sqlString.substring(4) + ")");
}
}
对用户拥有的每个SysRole都进行检查,
全部数据权限
如果是全部数据权限直接break跳出循环,
自定义数据权限
我们可以看到这里是在sys_role_dept这个表里去检查对应的角色对应的部门Id,然后用in进行查询
本部门数据权限
这个比较简单,直接等于用户的部门id就可以了
本部门及子部门权限
WHERE dept_id = {} or find_in_set( {} , ancestors )中的dept_id = {}很好理解,直接是本部门的权限,那么后面的find_in_set( {} , ancestors )呢?这是什么意思?
这里需要来看一下对应的表格式
首先
sys_dept表是有一个祖级列表ancestors这么一个字段的,它里面存放了该部门的所有父级Id,find_in_set会进行精确匹配,找到本部门的子级。顺便说一句,这个find_in_set不走索引。。。