目录
[1. 用户请求:用户访问用户列表接口 /system/user/list](#1. 用户请求:用户访问用户列表接口 /system/user/list)
[2. Controller:接收请求,调用Service层方法](#2. Controller:接收请求,调用Service层方法)
[3. AOP拦截:发现方法上有DataScope注解,执行切面逻辑](#3. AOP拦截:发现方法上有DataScope注解,执行切面逻辑)
[1. 切面入口:Before 注解](#1. 切面入口:Before 注解)
[2. 清空旧权限参数:clearDataScope](#2. 清空旧权限参数:clearDataScope)
[3. 处理数据权限:handleDataScope](#3. 处理数据权限:handleDataScope)
[4. SQL执行:将权限条件拼接到原始SQL中](#4. SQL执行:将权限条件拼接到原始SQL中)
[1、创建数据库表 ------ 设备信息表](#1、创建数据库表 —— 设备信息表)
[3、手动增强 ------ 添加数据权限支持](#3、手动增强 —— 添加数据权限支持)
[① 给 Service 方法加上 @DataScope 注解](#① 给 Service 方法加上 @DataScope 注解)
[② 检查 Mapper XML 是否支持 params.dataScope](#② 检查 Mapper XML 是否支持 params.dataScope)
[① 添加用户](#① 添加用户)
[② 角色权限分配](#② 角色权限分配)
[① admin------超级管理员](#① admin——超级管理员)
[② ry------普通用户(测试部门)](#② ry——普通用户(测试部门))
[③ lwy------普通用户(财务部门)](#③ lwy——普通用户(财务部门))
一、一个简单的需求
想象一下,你是一家公司的IT管理员,现在需要实现这样的功能:
- 老板(admin)可以看到公司所有部门的数据
- 部门经理只能看到自己部门的数据
- 普通员工只能看到自己的数据
这个功能在若依框架中是如何实现的呢?让我们一步步来解析。
二、核心实现原理
若依通过AOP(面向切面编程)和MyBatis的动态SQL实现了数据权限控制。简单来说,就是在执行数据库查询前,动态地修改SQL语句,添加权限过滤条件。
三、代码实现详解
1. 用户请求 :用户访问用户列表接口 /system/user/list
代码位置 :ruoyi-module-system/src/main/java/com/ruoyi/web/controller/system/SysUserController.java



2. Controller:接收请求,调用Service层方法
代码位置 :ruoyi-module-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java

3. AOP拦截:发现方法上有DataScope注解,执行切面逻辑
代码位置:
ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java
- DataScopeAspect.doBefore() - 观察切面是否被触发
- DataScopeAspect.dataScopeFilter() - 观察权限判断逻辑
- SysUserMapper.xml中的SQL语句 - 观察最终生成的SQL
java
@Aspect
@Component
public class DataScopeAspect {
// 数据权限过滤关键字
public static final String DATA_SCOPE = "dataScope";
/**
* 数据权限过滤
*/
@Before("@annotation(controllerDataScope)")
public void doBefore(JoinPoint point, DataScope controllerDataScope) {
// 1. 清空之前的数据权限参数,防止重复添加
clearDataScope(point);
// 2. 处理数据权限
handleDataScope(point, controllerDataScope);
}
protected void handleDataScope(final JoinPoint joinPoint, DataScope controllerDataScope) {
// 3. 获取当前登录用户
LoginUser loginUser = SecurityUtils.getLoginUser();
if (StringUtils.isNotNull(loginUser)) {
SysUser currentUser = loginUser.getUser();
// 4. 如果不是管理员,才需要过滤数据
if (StringUtils.isNotNull(currentUser) && !currentUser.isAdmin()) {
// 5. 数据权限过滤
dataScopeFilter(joinPoint, currentUser,
controllerDataScope.deptAlias(),
controllerDataScope.userAlias());
}
}
}
/**
* 数据范围过滤
*/
public static void dataScopeFilter(JoinPoint joinPoint, SysUser user,
String deptAlias, String userAlias) {
StringBuilder sqlString = new StringBuilder();
// 6. 遍历用户的所有角色
for (SysRole role : user.getRoles()) {
// 7. 获取角色的数据范围
String dataScope = role.getDataScope();
// 8. 根据数据范围类型拼接SQL
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 {
// 如果没有设置用户表别名,则不返回任何数据
sqlString.append(" OR 1=0 ");
}
}
}
// 9. 如果有数据权限条件
if (StringUtils.isNotBlank(sqlString.toString())) {
// 10. 获取方法参数
Object params = joinPoint.getArgs()[0];
if (StringUtils.isNotNull(params) && params instanceof BaseEntity) {
// 11. 将数据权限条件放入params中
BaseEntity baseEntity = (BaseEntity) params;
baseEntity.getParams().put(DATA_SCOPE, " AND (" + sqlString.substring(4) + ")");
}
}
}
}
📌AOP切面处理流程详解

1. 切面入口:Before 注解

作用:
- 在目标方法执行前进行拦截
- 清空可能存在的旧数据权限参数
- 调用权限处理方法
2. 清空旧权限参数:clearDataScope

3. 处理数据权限:handleDataScope

4、权限类型与SQL生成

4. SQL执行:将权限条件拼接到原始SQL中
代码位置 :ruoyi-module-system/src/main/resources/mapper/system/SysUserMapper.xml

${params.dataScope}是动态SQL片段,会被替换为数据权限条件- 例如:
AND (d.dept_id = 105)

四、车间设备数据权限
1、创建数据库表 ------ 设备信息表

2、使用若依**「代码生成功能」来快速完成设备信息模块**


3、手动增强 ------ 添加数据权限支持
① 给 Service 方法加上 @DataScope 注解

② 检查 Mapper XML 是否支持 params.dataScope

4、配置角色的数据权限
① 添加用户

② 角色权限分配

5、分别登录三个账号测试:
① admin------超级管理员

② ry------普通用户(测试部门)

③ lwy------普通用户(财务部门)
