在实际应用中,公司会有多个部门存在,每个部门的职能不一样,看到的数据不一样,每个部门也会分层级,例如普通员工职能看到自己创建的数据,部门领导可以看到该部门下的所有员工数据,系统需要实现这一逻辑,就是数据权限功能
1、功能实现操作界面
第一步:点击右侧菜单导航栏,点击角色管理
第二步:点击角色右侧的更多的数据权限
数据权限分为:全部数据、自定义数据、本部门数据、本部门以及一下数据、仅本人数据权限。
2、数据权限配置
1、如下是弹窗显示数据权限代码
<!-- 分配角色数据权限对话框 -->
<el-dialog :title="title" :visible.sync="openDataScope" width="500px" append-to-body>
<el-form :model="form" label-width="80px">
<el-form-item label="角色名称">
<el-input v-model="form.roleName" :disabled="true" />
</el-form-item>
<el-form-item label="权限字符">
<el-input v-model="form.roleKey" :disabled="true" />
</el-form-item>
<el-form-item label="权限范围">
<el-select v-model="form.dataScope" @change="dataScopeSelectChange">
<el-option
v-for="item in dataScopeOptions"
:key="item.value"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="数据权限" v-show="form.dataScope == 2">
<el-checkbox v-model="deptExpand" @change="handleCheckedTreeExpand($event, 'dept')">展开/折叠</el-checkbox>
<el-checkbox v-model="deptNodeAll" @change="handleCheckedTreeNodeAll($event, 'dept')">全选/全不选</el-checkbox>
<el-checkbox v-model="form.deptCheckStrictly" @change="handleCheckedTreeConnect($event, 'dept')">父子联动</el-checkbox>
<el-tree
class="tree-border"
:data="deptOptions"
show-checkbox
default-expand-all
ref="dept"
node-key="id"
:check-strictly="!form.deptCheckStrictly"
empty-text="加载中,请稍候"
:props="defaultProps"
></el-tree>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitDataScope">确 定</el-button>
<el-button @click="cancelDataScope">取 消</el-button>
</div>
</el-dialog>
2、下面是提交数据权限代码
/** 提交按钮(数据权限) */
submitDataScope: function() {
if (this.form.roleId != undefined) {
this.form.deptIds = this.getDeptAllCheckedKeys();
dataScope(this.form).then(response => {
this.$modal.msgSuccess("修改成功");
this.openDataScope = false;
this.getList();
});
}
},
3、后台api接口处理修改数据权限
1、如下是接口代码
/**
* 修改保存数据权限
*/
@PreAuthorize("@ss.hasPermi('system:role:edit')")
@Log(title = "角色管理", businessType = BusinessType.UPDATE)
@PutMapping("/dataScope")
public AjaxResult dataScope(@RequestBody SysRole role)
{
roleService.checkRoleAllowed(role);
roleService.checkRoleDataScope(role.getRoleId());
return toAjax(roleService.authDataScope(role));
}
业务逻辑说明:
1)、检查角色是否允许操作,针对登录者操作,具体代码roleService.checkRoleAllowed(role);
2)、校验角色是否有数据权限,roleService.checkRoleDataScope(role.getRoleId());
3)、修改数据权限信息,就是将角色绑定到角色部门表,并保存数据权限
如下是角色数据库表:
CREATE TABLE `sys_role` (
`role_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '角色ID',
`role_name` varchar(30) NOT NULL COMMENT '角色名称',
`role_key` varchar(100) NOT NULL COMMENT '角色权限字符串',
`role_sort` int(4) NOT NULL COMMENT '显示顺序',
`data_scope` char(1) DEFAULT '1' COMMENT '数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限)',
`menu_check_strictly` tinyint(1) DEFAULT '1' COMMENT '菜单树选择项是否关联显示',
`dept_check_strictly` tinyint(1) DEFAULT '1' COMMENT '部门树选择项是否关联显示',
`status` char(1) NOT NULL COMMENT '角色状态(0正常 1停用)',
`del_flag` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)',
`create_by` varchar(64) DEFAULT '' COMMENT '创建者',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_by` varchar(64) DEFAULT '' COMMENT '更新者',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`role_id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COMMENT='角色信息表';
data_scope:数据范围。
4、具体数据权限实现方式
在执行mysql到时候,通过spring 的aop切面,在执行sql前,追加数据过滤的sql语句。
具体代码如下
代码路径是在ruoyi-framewro模块下的
package com.ruoyi.framework.aspectj;
DataScopeAspect
/**
* 数据范围过滤
*
* @param joinPoint 切点
* @param user 用户
* @param deptAlias 部门别名
* @param userAlias 用户别名
* @param permission 权限字符
*/
public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String deptAlias, String userAlias, String permission)
{
StringBuilder sqlString = new StringBuilder();
List<String> conditions = new ArrayList<String>();
for (SysRole role : user.getRoles())
{
String dataScope = role.getDataScope();
if (!DATA_SCOPE_CUSTOM.equals(dataScope) && conditions.contains(dataScope))
{
continue;
}
if (StringUtils.isNotEmpty(permission) && StringUtils.isNotEmpty(role.getPermissions())
&& !StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission)))
{
continue;
}
if (DATA_SCOPE_ALL.equals(dataScope))
{
sqlString = new StringBuilder();
conditions.add(dataScope);
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(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias));
}
}
conditions.add(dataScope);
}
// 多角色情况下,所有角色都不包含传递过来的权限字符,这个时候sqlString也会为空,所以要限制一下,不查询任何数据
if (StringUtils.isEmpty(conditions))
{
sqlString.append(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias));
}
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) + ")");
}
}
}
如上述代码,根据数据库配置的data_scope字段,决定增加哪些sql语句。