在企业级应用开发中,基础的菜单与操作权限控制往往无法满足复杂的业务安全需求。例如,除了控制用户能访问哪些页面(角色菜单权限),系统还需根据用户的所属部门、职位等属性,精细化控制其可操作的数据范围,这就是数据权限隔离。
对于简单的数据过滤,最直接的思路是在每个查询的SQL中手动添加诸如 WHERE dept_id = ? 的条件。然而,当这类需求遍布系统的各个查询接口时,在每个 Service 或 Mapper 中重复编写权限拼接逻辑,会导致代码严重冗余、维护成本高昂,且容易出错。
本文将介绍一种基于 SpringBoot 和 AOP(面向切面编程)的统一解决方案,其核心思想是 “声明式注解 + SQL占位符动态替换”,有效实现权限逻辑与业务逻辑的解耦。该方案也是国内流行的 RuoYi 等开源框架所采用的数据权限实现方式。
核心原理:AOP拦截与动态SQL拼接
该方案通过自定义注解标记需要拦截的方法,并利用 AOP 拦截器在方法执行前,动态生成数据过滤的SQL片段,将其填充到预先在Mapper XML中预留的占位符位置。整个过程主要由三个核心组件协作完成:
@DataScope 注解:用于标记需要进行数据权限过滤的Service或Mapper方法。
${params.dataScope} 占位符:在MyBatis的Mapper XML文件的SQL语句中预留,作为权限条件片段的插入点。
- 数据权限拦截器 (
DataScopeInterceptor):核心处理组件,负责解析当前用户权限、生成SQL条件片段并替换占位符。
权限定义(角色表):

注解定义:

执行流程示例(以“查询用户列表”为例):
当一个携带了@DataScope注解的方法被调用时,框架将自动触发以下流程:
- 请求拦截:
DataScopeInterceptor 检测到该方法带有 @DataScope 注解,对其进行拦截。
- 获取权限上下文:从当前会话(如SecurityContext或ThreadLocal)中获取登录用户的详细信息,包括用户ID、所属部门ID及其拥有的角色。
- 生成SQL条件:根据用户角色所配置的数据权限范围,动态生成对应的SQL条件片段。例如:
- 权限为“全部数据”:生成空字符串(不添加任何限制)。
- 权限为“本部门及以下”(用户部门ID=103):生成
AND (d.dept_id = 103 OR d.ancestors LIKE '%,103,%')。
- 权限为“仅本人”(用户ID=2):生成
AND u.user_id = 2。
- 替换与执行:将生成的SQL条件片段,替换到Mapper XML中
${params.dataScope} 占位符的位置,最终组装成完整的SQL语句并执行查询。
实战:三步实现数据权限控制
下面通过一个查询用户列表的案例,具体说明如何应用此方案。
步骤1:在Service方法上添加注解
在需要进行数据权限控制的业务方法上添加 @DataScope 注解,并通过参数指明SQL中部门表与用户表的别名(需与XML中的别名保持一致)。
/**
* 查询用户列表(需数据权限过滤)
* @DataScope 标记该方法需要数据权限控制
* deptAlias="d":对应XML中部门表别名 `d`
* userAlias="u":对应XML中用户表别名 `u`
*/
@DataScope(deptAlias = "d", userAlias = "u")
public List<SysUser> selectUserList(SysUser user) {
return userMapper.selectUserList(user);
}
步骤2:在Mapper XML中预留占位符
在对应的查询SQL的 WHERE 子句末尾,添加 ${params.dataScope} 占位符。框架会自动将生成的权限条件拼接在此处。
<select id="selectUserList" parameterType="com.ruoyi.system.domain.SysUser" resultMap="SysUserResult">
SELECT u.user_id, u.user_name, d.dept_name
FROM sys_user u
LEFT JOIN sys_dept d ON u.dept_id = d.dept_id
WHERE u.del_flag = '0'
${params.dataScope} <!-- 数据权限条件将动态插入此处 -->
</select>
步骤3:后台配置角色数据权限
在系统管理后台的角色管理页面,为不同角色配置其数据权限范围,例如:
- 超级管理员:配置为“全部数据”。
- 部门负责人:配置为“本部门及以下数据”。
- 普通员工:配置为“仅本人数据”。
不同权限下的SQL生成效果
场景一:超级管理员(权限:全部数据)
场景二:部门负责人(权限:本部门及以下,部门ID=103)
- 生成的
${params.dataScope} 内容:AND (d.dept_id = 103 OR d.ancestors LIKE '%,103,%')
- 最终执行SQL:
SELECT u.user_id, u.user_name, d.dept_name
FROM sys_user u
LEFT JOIN sys_dept d ON u.dept_id = d.dept_id
WHERE u.del_flag = '0'
AND (d.dept_id = 103 OR d.ancestors LIKE '%,103,%')
场景三:普通员工(权限:仅本人,用户ID=2)
总结
通过“注解声明 + AOP拦截 + 占位符替换”的模式,我们成功将分散在各处的数据权限校验逻辑集中到了一处。开发者只需通过一个简单的注解来标识需要控制的方法,并通过后台配置赋予角色相应的数据范围,即可实现从“全部数据”到“仅本人数据”等多级粒度的权限隔离。这种方案极大地提升了代码的复用性和可维护性,是构建安全、灵活的后台管理系统的有效实践。
|