当前位置: 首页 > news >正文

java每日精进 7.29【框架数据权限详解】

数据权限控制执行步骤流程,以system模块的配置为例

1.加载springbean

1.YudaoDeptDataPermissionAutoConfiguration和 DataPermissionConfiguration

  1. 系统启动时,Spring Boot 扫描并加载所有标注了 @Configuration 或 @AutoConfiguration 的配置类。
    • YudaoDeptDataPermissionAutoConfiguration 和 DataPermissionConfiguration 被加载。
/*** 基于部门的数据权限 AutoConfiguration* YudaoDeptDataPermissionAutoConfiguration(在满足条件时)创建 DeptDataPermissionRule Bean,* 并通过 DeptDataPermissionRuleCustomizer 配置部门相关规则。*/
@AutoConfiguration
@ConditionalOnClass(LoginUser.class)
@ConditionalOnBean(value = {PermissionApi.class, DeptDataPermissionRuleCustomizer.class})
public class YudaoDeptDataPermissionAutoConfiguration {/*** 创建一个 DeptDataPermissionRule 对象,并将 permissionApi 作为参数传递给其构造函数。* 遍历 customizers 列表,调用每个 DeptDataPermissionRuleCustomizer 的 customize 方法* 对 rule 对象进行自定义配置。* 返回配置好的 DeptDataPermissionRule 对象。* @param permissionApi* @param customizers* @return*/@Beanpublic DeptDataPermissionRule deptDataPermissionRule(PermissionApi permissionApi,List<DeptDataPermissionRuleCustomizer> customizers) {// 创建 DeptDataPermissionRule 对象DeptDataPermissionRule rule = new DeptDataPermissionRule(permissionApi);// 补全表配置customizers.forEach(customizer -> customizer.customize(rule));return rule;}
}
/*** system 模块的数据权限 Configuration*/
@Configuration(proxyBeanMethods = false)
public class DataPermissionConfiguration {@Beanpublic DeptDataPermissionRuleCustomizer sysDeptDataPermissionRuleCustomizer() {return rule -> {// deptrule.addDeptColumn(AdminUserDO.class);rule.addDeptColumn(DeptDO.class, "id");// userrule.addUserColumn(AdminUserDO.class, "id");};}
}
  • DataPermissionConfiguration 类定义了 system 模块的权限规则

  • AdminUserDO 表的 dept_id 字段用于部门过滤。
  • DeptDO 表的 id 字段用于部门过滤。
  • AdminUserDO 表的 id 字段用于用户过滤。

  • YudaoDeptDataPermissionAutoConfiguration 创建 DeptDataPermissionRule 对象,注入 PermissionCommonApi。
  • 获取所有 DeptDataPermissionRuleCustomizer(包括 DataPermissionConfiguration 提供的 sysDeptDataPermissionRuleCustomizer)
  • 调用 customizer.customize(rule),执行:
  • rule.addDeptColumn(AdminUserDO.class):添加 admin_user 表的 dept_id 字段。
  • rule.addDeptColumn(DeptDO.class, "id"):添加 dept 表的 id 字段。
  • rule.addUserColumn(AdminUserDO.class, "id"):添加 admin_user 表的 id 字段。
  • 即system模块的DataPermissionConfiguration中的代码,添加类及其数据权限逻对应的字段

2.创建 DeptDataPermissionRule 对象

  • YudaoDeptDataPermissionAutoConfiguration 类,执行以下逻辑:
    • 检查 LoginUser 类和 PermissionCommonApi、DeptDataPermissionRuleCustomizer 的 Bean 是否存在(通过 @ConditionalOnClass 和 @ConditionalOnBean)。
    • 调用其 deptDataPermissionRule 方法,创建 DeptDataPermissionRule 对象:
      • 构造 DeptDataPermissionRule 实例,注入 PermissionCommonApi(用于查询权限)。
      • 获取所有 DeptDataPermissionRuleCustomizer 的 Bean(包括 DataPermissionConfiguration 提供的 sysDeptDataPermissionRuleCustomizer)。

3.应用权限规则配置

  • DataPermissionConfiguration 定义了 system 模块的权限规则,通过 sysDeptDataPermissionRuleCustomizer 方法:
    • 返回一个 DeptDataPermissionRuleCustomizer 实例,调用其 customize 方法:
return rule -> {rule.addDeptColumn(AdminUserDO.class); // 配置 admin_user 表的 dept_id 字段rule.addDeptColumn(DeptDO.class, "id"); // 配置 dept 表的 id 字段rule.addUserColumn(AdminUserDO.class, "id"); // 配置 admin_user 表的 id 字段
};
    • 执行 addDeptColumn(AdminUserDO.class):
      • 调用 TableInfoHelper.getTableInfo(AdminUserDO.class),获取表名 admin_user。
      • 执行 addDeptColumn("admin_user", "dept_id"),将 {"admin_user": "dept_id"} 存入 deptColumns,将 admin_user 添加到 TABLE_NAMES。
    • 执行 addDeptColumn(DeptDO.class, "id"):
      • 获取表名 dept,将 {"dept": "id"} 存入 deptColumns,将 dept 添加到 TABLE_NAMES。
    • 执行 addUserColumn(AdminUserDO.class, "id"):
      • 获取表名 admin_user,将 {"admin_user": "id"} 存入 userColumns,admin_user 已存在于 TABLE_NAMES,不重复添加。
  • 结果:TABLE_NAMES = {"admin_user", "dept"},deptColumns = {"admin_user": "dept_id", "dept": "id"},userColumns = {"admin_user": "id"}。
    public void addDeptColumn(Class<? extends BaseDO> entityClass) {addDeptColumn(entityClass, DEPT_COLUMN_NAME);}public void addDeptColumn(Class<? extends BaseDO> entityClass, String columnName) {String tableName = TableInfoHelper.getTableInfo(entityClass).getTableName();addDeptColumn(tableName, columnName);}public void addDeptColumn(String tableName, String columnName) {deptColumns.put(tableName, columnName);TABLE_NAMES.add(tableName);}public void addUserColumn(Class<? extends BaseDO> entityClass) {addUserColumn(entityClass, USER_COLUMN_NAME);}public void addUserColumn(Class<? extends BaseDO> entityClass, String columnName) {String tableName = TableInfoHelper.getTableInfo(entityClass).getTableName();addUserColumn(tableName, columnName);}public void addUserColumn(String tableName, String columnName) {userColumns.put(tableName, columnName);TABLE_NAMES.add(tableName);}

4.注册 DeptDataPermissionRule

  • YudaoDeptDataPermissionAutoConfiguration 将配置好的 DeptDataPermissionRule 注册为 Spring Bean,供框架使用。

2. 用户登录

  1. 用户登录系统

    • 用户(假设 ID 为 123,类型为管理员)登录系统,框架通过 SecurityFrameworkUtils 创建 LoginUser 对象,存储用户 ID、类型等信息。
    • LoginUser 包含一个上下文(Context),用于缓存权限数据(如 DeptDataPermissionRespDTO)。

3. 用户执行查询

  1. 用户发起查询

    • 用户执行 SQL 查询:SELECT * FROM admin_user。
    • 框架(通过 MyBatis 拦截器或类似机制)拦截查询,解析 SQL,提取表名 admin_user 和表别名(这里为 null,因为没有 AS 别名)。
  2. 检查表是否需要权限控制

    • 框架调用 DeptDataPermissionRule.getTableNames(),返回 TABLE_NAMES = {"admin_user", "dept"}。
    • 确认 admin_user 在 TABLE_NAMES 中,触发数据权限控制逻辑。
  3. 调用 getExpression 生成权限条件

public Expression getExpression(String tableName, Alias tableAlias) {// 只有有登陆用户的情况下,才进行数据权限的处理LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();if (loginUser == null) {return null;}// 只有管理员类型的用户,才进行数据权限的处理if (ObjectUtil.notEqual(loginUser.getUserType(), UserTypeEnum.ADMIN.getValue())) {return null;}// 获得数据权限DeptDataPermissionRespDTO deptDataPermission = loginUser.getContext(CONTEXT_KEY, DeptDataPermissionRespDTO.class);// 从上下文中拿不到,则调用逻辑进行获取if (deptDataPermission == null) {deptDataPermission = permissionApi.getDeptDataPermission(loginUser.getId());if (deptDataPermission == null) {log.error("[getExpression][LoginUser({}) 获取数据权限为 null]", JsonUtils.toJsonString(loginUser));throw new NullPointerException(String.format("LoginUser(%d) Table(%s/%s) 未返回数据权限",loginUser.getId(), tableName, tableAlias.getName()));}// 添加到上下文中,避免重复计算loginUser.setContext(CONTEXT_KEY, deptDataPermission);}// 情况一,如果是 ALL 可查看全部,则无需拼接条件if (deptDataPermission.getAll()) {return null;}// 情况二,即不能查看部门,又不能查看自己,则说明 100% 无权限if (CollUtil.isEmpty(deptDataPermission.getDeptIds())&& Boolean.FALSE.equals(deptDataPermission.getSelf())) {return new EqualsTo(null, null); // WHERE null = null,可以保证返回的数据为空}// 情况三,拼接 Dept 和 User 的条件,最后组合Expression deptExpression = buildDeptExpression(tableName,tableAlias, deptDataPermission.getDeptIds());Expression userExpression = buildUserExpression(tableName, tableAlias, deptDataPermission.getSelf(), loginUser.getId());if (deptExpression == null && userExpression == null) {// TODO 芋艿:获得不到条件的时候,暂时不抛出异常,而是不返回数据log.warn("[getExpression][LoginUser({}) Table({}/{}) DeptDataPermission({}) 构建的条件为空]",JsonUtils.toJsonString(loginUser), tableName, tableAlias, JsonUtils.toJsonString(deptDataPermission));
//            throw new NullPointerException(String.format("LoginUser(%d) Table(%s/%s) 构建的条件为空",
//                    loginUser.getId(), tableName, tableAlias.getName()));return EXPRESSION_NULL;}if (deptExpression == null) {return userExpression;}if (userExpression == null) {return deptExpression;}// 目前,如果有指定部门 + 可查看自己,采用 OR 条件。即,WHERE (dept_id IN ? OR user_id = ?)return new ParenthesedExpressionList(new OrExpression(deptExpression, userExpression));}
    • 框架调用 DeptDataPermissionRule.getExpression("admin_user", null):
      • 获取登录用户
        • 执行 SecurityFrameworkUtils.getLoginUser(),返回 LoginUser(ID 为 123,类型为 UserTypeEnum.ADMIN)。
      • 检查用户类型
        • 确认 loginUser.getUserType() == UserTypeEnum.ADMIN.getValue(),继续处理。
      • 获取权限数据
        • 检查 LoginUser 的上下文,尝试获取 CONTEXT_KEY(DeptDataPermissionRule)对应的 DeptDataPermissionRespDTO。DeptDataPermissionRespDTO {
        • deptIds: [10, 20], // 可访问部门 10 和 20

          self: true, // 可查看自己的数据

          all: false // 不可查看所有数据

          }

        • 将权限数据存入上下文:loginUser.setContext(CONTEXT_KEY, deptDataPermission)。
      • 检查权限
        • deptDataPermission.getAll() == false,需要生成条件。
        • deptIds 不为空([10, 20]),self == true,需要生成部门和用户条件。
      • 生成部门条件
        • 调用 buildDeptExpression("admin_user", null, [10, 20]):
          • 检查 deptColumns,找到 admin_user: dept_id。
          • deptIds 不为空,生成 InExpression:dept_id IN (10, 20)。
      • 生成用户条件
        • 调用 buildUserExpression("admin_user", null, true, 123):
          • self == true,检查 userColumns,找到 admin_user: id。
          • 生成 EqualsTo:id = 123。
      • 合并条件
        • 部门和用户条件都存在,生成 OrExpression:(dept_id IN (10, 20) OR id = 123)。
        • 用 ParenthesedExpressionList 包裹,返回最终条件。
  1. 修改 SQL 查询
    • 框架将生成的条件 (dept_id IN (10, 20) OR id = 123) 加入原 SQL:
      • 原查询:SELECT * FROM admin_user。
      • 修改后:SELECT * FROM admin_user WHERE (dept_id IN (10, 20) OR id = 123)。
  2. 执行查询并返回结果
    • MyBatis 执行修改后的 SQL,只返回部门 ID 为 10 或 20 的记录,以及用户 ID 为 123 的记录。
http://www.dtcms.com/a/303830.html

相关文章:

  • 远程Qt Creator中文输入解决方案
  • day064-kodbox接入对象存储与配置负载均衡
  • linux命令tail的实际应用
  • 网络数据传输与NAT技术的工作原理
  • 社区老人健康信息管理系统|基于springboot社区老人健康信息管理系统设计与实现(源码+数据库+文档)
  • SSO CAS+Shiro+springmvc单点登录解决方案
  • 符号计算与算法实践|使用Maple教授​​群论​​和​​图论​​课程
  • 【 MySQL集群架构与实践1】使用Docker实现简单主从复制
  • uni-app x开发避坑指南:拯救被卡顿的UI线程!
  • 【CF】Day114——杂题 (贪心 + 图论 | LCM + 贪心 | 最大最小子序列 + 图论)
  • 图论:Bellman_ford算法
  • docker设置iptables=false后容器内部无法互相访问
  • vue3组件通信的几种方法,详解
  • 工业补贴携手华为云,重塑汽车零部件供应链管理新趋势
  • B 站搜一搜关键词优化:精准触达用户的流量密码
  • 51c大模型~合集161
  • SQL注入SQLi-LABS 靶场less26-30详细通关攻略
  • Elasticsearch 深度分页问题与 `search_after` 解决方案
  • 从centos更换至ubuntu的安装、配置、操作记录
  • cpolar 内网穿透 ubuntu 使用石
  • 知识点梳理
  • 基于三台主机搭建 Web 服务环境:Nginx、NFS 与 DNS 配置全流程
  • Python实用代码示例大全
  • tcp通讯学习数据传输
  • Effective C++ 条款10:令operator=返回一个reference to *this
  • 渗透测试与漏洞扫描有什么区别?
  • napping靶机教程(超详细讲解)
  • Leetcode-3152 特殊数组 II
  • TCPDump实战手册:协议/端口/IP过滤与组合分析指南
  • 【Java面试题】面向对象