首页 > Spring > 当前页面

Mybatis DataPermissionInterceptor实现数据权限详解

2026-06-14 NEW个对象

📌 Mybatis DataPermissionInterceptor实现数据权限详解

在企业级管理系统中,数据权限(Data Permission)是权限体系的重要组成部分。功能权限解决“能不能访问页面”,而数据权限解决“能看到哪些数据”。Mybatis通过拦截器机制,可以在SQL执行前动态修改SQL,从而实现行级数据权限控制。

1️⃣ 问题背景

在RBAC权限模型中,角色可以控制菜单、按钮、接口访问权限,但很多场景下仅有功能权限远远不够。

例如:

  • 校长可以查看全校学生信息
  • 院长只能查看本学院学生信息
  • 辅导员只能查看自己管理班级学生信息
  • 教师只能查看自己授课班级学生信息
  • 销售经理可以查看部门所有客户
  • 普通销售只能查看自己的客户

如果没有数据权限,那么拥有查询接口权限的用户可能看到全部数据,造成严重的信息泄露问题。

⚠️ 功能权限控制的是接口访问,数据权限控制的是数据范围,两者缺一不可。

2️⃣ 核心原理

Mybatis提供了Interceptor插件机制,可以拦截Executor、StatementHandler、ParameterHandler、ResultSetHandler等核心组件。

数据权限最常见的实现方式就是拦截SQL执行过程,在SQL发送到数据库之前动态追加权限条件。

用户发起请求

进入Controller

进入Service

调用Mapper

DataPermissionInterceptor拦截SQL

追加权限条件

执行数据库查询

返回符合权限的数据

例如原始SQL:

select * from student

经过数据权限拦截后:

select * from student where college_id = 1001

数据库最终只返回当前用户有权限查看的数据。

3️⃣ 数据结构分析

用户表

sys_user
id
username
dept_id

角色表

sys_role
id
role_name
data_scope

角色数据权限字段

data_scope通常定义以下几种权限:

  • ALL:全部数据权限
  • DEPT:本部门数据
  • DEPT_AND_CHILD:本部门及子部门
  • SELF:本人数据
  • CUSTOM:自定义数据权限

例如:

管理员 → ALL
院长 → DEPT
教师 → SELF

4️⃣ 算法分析

DataPermissionInterceptor本质上就是SQL改写算法。

核心流程如下:

获取当前用户

获取用户角色

获取数据权限范围

解析原始SQL

拼接权限条件

替换BoundSql中的SQL

继续执行

例如:

原SQL:

select * from student where status=1

当前用户属于计算机学院:

college_id = 1001

拼接后:

select * from student
where status=1
and college_id=1001

5️⃣ 执行流程

第一步:定义数据权限注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DataPermission {
  String value();
}

Mapper方法增加注解:

@DataPermission("student")
List<Student> selectList();

第二步:定义拦截器

@Intercepts({
@Signature(
type=StatementHandler.class,
method="prepare",
args={Connection.class,Integer.class}
)})
public class DataPermissionInterceptor
implements Interceptor{ }

第三步:获取当前SQL

BoundSql boundSql =
statementHandler.getBoundSql();

String sql = boundSql.getSql();

第四步:获取用户信息

LoginUser loginUser =
SecurityContextHolder.getUser();

第五步:拼接权限SQL

sql += " and dept_id="+
loginUser.getDeptId();

第六步:替换SQL

MetaObject metaObject=
SystemMetaObject.forObject(statementHandler);

metaObject.setValue(
"delegate.boundSql.sql",
newSql
);

至此完成SQL动态改写。

6️⃣ 实际案例

案例一:教师查看学生

原SQL:

select * from student

当前教师ID:

teacher_id = 10086

改写后:

select * from student
where teacher_id=10086

案例二:部门经理查看员工

部门ID:

dept_id = 20

改写后:

select * from employee
where dept_id=20

案例三:集团管理员

管理员拥有ALL权限。

此时不需要拼接任何SQL。

select * from employee

直接执行即可。

7️⃣ 优缺点分析

优点

  • 业务代码零侵入
  • 统一权限控制
  • 开发成本低
  • 扩展性强
  • 适合大型管理系统
  • 支持RBAC权限体系
  • 支持多租户扩展

缺点

  • SQL解析复杂
  • 多表关联处理麻烦
  • 动态SQL容易出错
  • 性能有一定损耗
  • 复杂查询维护成本较高
✅ 生产环境通常不会直接字符串拼接SQL,而是借助JSqlParser解析SQL AST后进行安全改写。

8️⃣ 面试常见问题

面试题1:为什么选择Mybatis拦截器实现数据权限?

因为可以在SQL执行前统一修改SQL,实现业务无侵入的数据权限控制。

面试题2:拦截器拦截哪个对象?

通常拦截StatementHandler.prepare方法,因为此时SQL已经生成但尚未发送数据库。

面试题3:为什么很多框架使用JSqlParser?

直接字符串拼接容易导致SQL错误,JSqlParser可以将SQL解析为语法树,安全地增加Where条件。

面试题4:若SQL已经有Where怎么办?

需要判断SQL结构,如果存在Where则追加AND,否则新增Where。

面试题5:若存在多表关联怎么办?

需要指定数据权限字段所属表别名,例如:

select *
from student s
left join class c
on s.class_id=c.id

and s.dept_id=10

面试题6:若使用Spring Security如何获取用户?

通常从SecurityContextHolder获取当前登录用户信息。

面试题7:若管理员拥有全部权限怎么办?

直接跳过SQL改写逻辑即可。

面试题8:Mybatis-Plus如何实现数据权限?

Mybatis-Plus提供DataPermissionInterceptor,可以结合JSqlParser自动完成SQL权限拼接。

9️⃣ 总结

📌 Mybatis数据权限的核心思想是在SQL执行前动态改写SQL。

📌 DataPermissionInterceptor本质是Mybatis插件机制的应用。

📌 拦截StatementHandler获取原始SQL,根据当前用户角色生成权限条件。

📌 将权限条件动态追加到Where子句,实现行级数据隔离。

📌 企业级项目通常采用“RBAC + 数据权限 + Mybatis拦截器 + JSqlParser”方案,实现统一的数据权限控制体系。

🚀 面试中如果能够讲清楚Interceptor拦截原理、BoundSql修改流程、JSqlParser解析机制以及Mybatis-Plus DataPermissionInterceptor实现原理,基本可以覆盖绝大多数中高级Java岗位的数据权限相关问题。

上一篇:

下一篇:RAG是什么?它的工作原理是什么?

相关文章

  • Mybatis DataPermissionInterceptor实现数据权限详解

    面试中如果能够讲清楚Interceptor拦截原理、BoundSql修改流程、JSqlParser解析机制以及Mybatis-Plus DataPermissionInterceptor实现原理,基本可以覆盖绝大多数中高级Java岗位的数据权限相关问题。

    NEW个对象 2026-06-14

NEW个对象 NEW个对象
JAVA是世界上最好的语言

推荐文章