找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

1153

积分

0

好友

162

主题
发表于 3 天前 | 查看: 4| 回复: 0

在企业级应用开发中,基础的菜单与操作权限控制往往无法满足复杂的业务安全需求。例如,除了控制用户能访问哪些页面(角色菜单权限),系统还需根据用户的所属部门、职位等属性,精细化控制其可操作的数据范围,这就是数据权限隔离。

对于简单的数据过滤,最直接的思路是在每个查询的SQL中手动添加诸如 WHERE dept_id = ? 的条件。然而,当这类需求遍布系统的各个查询接口时,在每个 ServiceMapper 中重复编写权限拼接逻辑,会导致代码严重冗余、维护成本高昂,且容易出错。

本文将介绍一种基于 SpringBoot 和 AOP(面向切面编程)的统一解决方案,其核心思想是 “声明式注解 + SQL占位符动态替换”,有效实现权限逻辑与业务逻辑的解耦。该方案也是国内流行的 RuoYi 等开源框架所采用的数据权限实现方式。

核心原理:AOP拦截与动态SQL拼接

该方案通过自定义注解标记需要拦截的方法,并利用 AOP 拦截器在方法执行前,动态生成数据过滤的SQL片段,将其填充到预先在Mapper XML中预留的占位符位置。整个过程主要由三个核心组件协作完成:

  1. @DataScope 注解:用于标记需要进行数据权限过滤的Service或Mapper方法。
  2. ${params.dataScope} 占位符:在MyBatis的Mapper XML文件的SQL语句中预留,作为权限条件片段的插入点。
  3. 数据权限拦截器 (DataScopeInterceptor):核心处理组件,负责解析当前用户权限、生成SQL条件片段并替换占位符。

权限定义(角色表):
角色数据权限字段

注解定义:
DataScope注解

执行流程示例(以“查询用户列表”为例):

当一个携带了@DataScope注解的方法被调用时,框架将自动触发以下流程:

  1. 请求拦截DataScopeInterceptor 检测到该方法带有 @DataScope 注解,对其进行拦截。
  2. 获取权限上下文:从当前会话(如SecurityContext或ThreadLocal)中获取登录用户的详细信息,包括用户ID、所属部门ID及其拥有的角色。
  3. 生成SQL条件:根据用户角色所配置的数据权限范围,动态生成对应的SQL条件片段。例如:
    • 权限为“全部数据”:生成空字符串(不添加任何限制)。
    • 权限为“本部门及以下”(用户部门ID=103):生成 AND (d.dept_id = 103 OR d.ancestors LIKE '%,103,%')
    • 权限为“仅本人”(用户ID=2):生成 AND u.user_id = 2
  4. 替换与执行:将生成的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生成效果

场景一:超级管理员(权限:全部数据)

  • 生成的 ${params.dataScope} 内容:(空字符串)
  • 最终执行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'

场景二:部门负责人(权限:本部门及以下,部门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)

  • 生成的 ${params.dataScope} 内容:AND u.user_id = 2
  • 最终执行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 u.user_id = 2

总结

通过“注解声明 + AOP拦截 + 占位符替换”的模式,我们成功将分散在各处的数据权限校验逻辑集中到了一处。开发者只需通过一个简单的注解来标识需要控制的方法,并通过后台配置赋予角色相应的数据范围,即可实现从“全部数据”到“仅本人数据”等多级粒度的权限隔离。这种方案极大地提升了代码的复用性和可维护性,是构建安全、灵活的后台管理系统的有效实践。




上一篇:Java内部类完全指南:从基础到实践,掌握嵌套、匿名与静态内部类
下一篇:基于FFPN网络的自适应视频预处理:优化H.265编码下UGC视频质量与码率
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2025-12-17 14:57 , Processed in 0.142951 second(s), 37 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

快速回复 返回顶部 返回列表