手把手调试 RuoYi-Vue-Plus 数据权限:用IDEA断点摸清 PlusDataPermissionInterceptor 的完整工作流
深度调试RuoYi-Vue-Plus数据权限:IDEA断点追踪全流程实战
当接手一个采用RuoYi-Vue-Plus框架的遗留项目时,数据权限模块往往是需要优先理解的核心功能之一。与传统的代码阅读相比,通过IDEA调试器实时追踪执行流程,能更直观地掌握 PlusDataPermissionInterceptor 如何动态改写SQL语句。本文将带您从发送测试请求开始,逐步设置关键断点,完整还原数据权限从注解解析到SQL重写的技术细节。
1. 环境准备与调试入口
在开始调试前,确保已配置好以下环境:
- JDK 1.8+ 与 IntelliJ IDEA 2021+
- 正常运行的RuoYi-Vue-Plus项目(建议使用3.5.0+版本)
- 示例Controller方法(如带
@DataPermission注解的查询接口)
关键调试启动步骤 :
- 在测试Controller方法上添加
@DataPermission注解
@DataPermission(deptAlias = "d", userAlias = "u")
@GetMapping("/list")
public TableDataInfo list(DemoEntity entity) {
// 业务逻辑
}
- 启动项目并打开Postman发送测试请求
- 在IDEA中全局搜索
PlusDataPermissionInterceptor类
提示:调试前建议关闭MyBatis二级缓存,避免旧SQL影响观察效果
2. 拦截器入口断点设置
在 PlusDataPermissionInterceptor 中设置以下关键断点:
2.1 beforeQuery方法断点
这是拦截器的第一个入口点,当执行Mapper查询时会触发。重点关注三个核心判断:
public void beforeQuery(Executor executor, MappedStatement ms,
Object parameter, RowBounds rowBounds, ResultHandler resultHandler,
BoundSql boundSql) {
// 断点1:检查是否忽略数据权限
if (InterceptorIgnoreHelper.willIgnoreDataPermission(ms.getId())) {
return;
}
// 断点2:验证权限有效性
if (dataPermissionHandler.isInvalid()) {
return;
}
// 断点3:SQL解析入口
parserSingle(boundSql, ms, parameter);
}
调试观察要点 :
ms.getId()值应匹配当前Mapper方法全限定名boundSql.getSql()原始SQL语句内容parameter传递的查询参数
2.2 processSelect方法断点
该方法负责处理SELECT语句的重写逻辑:
protected void processSelect(Select select, int index, String sql, Object obj) {
// 断点4:观察select对象结构
PlainSelect plainSelect = (PlainSelect) select.getSelectBody();
// 断点5:WHERE条件注入前
Expression where = plainSelect.getWhere();
// 断点6:调用权限处理器
setWhere(plainSelect, where, (MappedStatement) obj);
}
关键变量监控表 :
| 变量名 | 预期值类型 | 调试意义 |
|---|---|---|
| select | net.sf.jsqlparser.statement.select.Select | 解析后的SQL对象 |
| plainSelect.getWhere() | net.sf.jsqlparser.expression.Expression | 原始WHERE条件 |
| obj | MappedStatement | 当前Mapper语句元数据 |
3. 权限上下文追踪
数据权限的核心在于如何将用户信息转换为SQL条件。通过 DataPermissionHelper 的调试可以清晰看到这一过程:
3.1 用户信息获取流程
在 PlusDataPermissionHandler.getSqlSegment() 方法中设置断点:
public String getSqlSegment(String where, String mappedStatementId) {
// 断点7:获取当前用户
Object user = DataPermissionHelper.getVariable("user");
if (user == null) {
// 从数据库加载用户的逻辑
}
// 断点8:超级管理员判断
if (isAdmin(user)) {
return where;
}
// 断点9:构建数据过滤条件
return buildDataFilter(where, user, mappedStatementId);
}
典型调试场景 :
- 以admin用户登录时,直接返回原始WHERE条件
- 普通用户登录时,进入
buildDataFilter构建部门过滤条件
3.2 注解解析过程
在 findAnnotation 方法中观察注解缓存机制:
private DataPermission findAnnotation(String mappedStatementId) {
// 断点10:缓存查询
DataPermission dataPermission = dataPermissionCacheMap.get(mappedStatementId);
if (dataPermission == null) {
// 反射获取注解
dataPermission = AnnotationUtil.getAnnotation(/*...*/);
// 断点11:缓存写入
dataPermissionCacheMap.put(mappedStatementId, dataPermission);
}
return dataPermission;
}
注意:修改注解后需要重启应用或清除缓存才能生效
4. SQL重写全流程观察
最终的SQL改写发生在 setWhere 方法中,这是调试最关键的环节:
4.1 原始SQL与改写对比
通过对比 boundSql.getSql() 的前后变化,可以直观看到:
-- 原始SQL
SELECT * FROM sys_user
-- 改写后SQL(非管理员)
SELECT * FROM sys_user WHERE dept_id IN (100,101)
4.2 多条件拼接逻辑
当存在多个权限规则时,观察条件拼接方式:
// 在buildDataFilter方法中
StringBuilder dataFilter = new StringBuilder();
for (Role role : roles) {
dataFilter.append(role.getDataScope())
.append(" OR ");
}
// 最终处理后的WHERE条件
where = "(" + dataFilter.substring(0, dataFilter.length() - 4) + ")"
常见调试问题排查 :
- 条件缺失:检查
DataPermissionHelper中的用户信息是否正确 - SQL语法错误:观察
JSQLParser解析后的AST结构 - 注解不生效:确认
mappedStatementId是否匹配Mapper方法
通过这种沉浸式调试方法,不仅能理解数据权限的实现原理,更能掌握二次开发时的定制技巧。比如需要扩展权限维度时,可以重点修改 PlusDataPermissionHandler.buildDataFilter 的实现逻辑。
更多推荐


所有评论(0)