2026/4/16 22:20:36
网站建设
项目流程
玉溪哪有网站建设服务公司,wordpress主题字体大小,wordpress 遍历分类目录,建站网址打不开一、背景与目标
在若依框架原有 DataScope 的基础上#xff0c;实现一套独立的、基于部门层级的数据权限过滤机制#xff0c;用于按组织结构灵活控制数据可见范围。
设计目标不依赖角色、不判断是否管理员通过注解参数动态控制数据范围支持#xff1a;
是否包含本部门向上查…一、背景与目标在若依框架原有DataScope的基础上实现一套独立的、基于部门层级的数据权限过滤机制用于按组织结构灵活控制数据可见范围。设计目标不依赖角色、不判断是否管理员通过注解参数动态控制数据范围支持是否包含本部门向上查询 N 级部门向下查询 N 级部门 / 所有子部门与若依原有BaseEntity params MyBatis XML机制完全兼容二、核心设计思路1. 技术方案使用AOP 自定义注解拦截查询方法在方法执行前根据当前用户部门 ID动态拼接部门过滤 SQL注入到BaseEntity.params.dataScopeMapper XML 中通过${params.dataScope}拼接 WHERE 条件2. 依赖表结构sys_dept/* by 01130.hk - online tools website : 01130.hk/zh/endecodejs.html */ dept_id 部门ID parent_id 父部门ID ancestors 祖先路径如0,1,3,10三、自定义注解ExtendedDataScope/* by 01130.hk - online tools website : 01130.hk/zh/endecodejs.html */ Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) Documented public interface ExtendedDataScope { // 部门表别名必填 String deptAlias() default ; // 用户表别名预留扩展 String userAlias() default ; // 权限类型当前主要使用 dept String type() default dept; // 向上级部门层数0 不包含 int upLevel() default 0; // 向下级部门层数0 不包含999 所有子级 int downLevel() default 0; // 是否包含本部门 boolean includeSelf() default true; }四、AOP 实现要点1. 切面职责拦截所有标注ExtendedDataScope的方法清空历史 dataScope防止 SQL 注入生成部门层级 SQL写入BaseEntity.params.dataScope2. 核心处理流程Before ├─ clearDataScope() ├─ 获取当前用户 ├─ 读取注解参数 ├─ 构建部门范围 SQL └─ 写入 params.dataScope点击查看代码Aspect Component public class ExtendedDataScopeAspect { /** * 参数 key和若依一致 */ public static final String DATA_SCOPE dataScope; Before(annotation(dataScope)) public void doBefore(JoinPoint joinPoint, ExtendedDataScope dataScope) { clearDataScope(joinPoint); handleDataScope(joinPoint, dataScope); } private void handleDataScope(JoinPoint joinPoint, ExtendedDataScope dataScope) { LoginUser loginUser SecurityUtils.getLoginUser(); if (loginUser null) { return; } String deptAlias dataScope.deptAlias(); if (StringUtils.isBlank(deptAlias)) { return; } String sql buildDeptScopeSql(loginUser.getDeptId(), deptAlias, dataScope); if (StringUtils.isBlank(sql)) { return; } Object params joinPoint.getArgs()[0]; if (params instanceof BaseEntity) { BaseEntity baseEntity (BaseEntity) params; baseEntity.getParams().put(DATA_SCOPE, AND ( sql )); } } /** * 构建部门层级 SQL */ private String buildDeptScopeSql(Long deptId, String deptAlias, ExtendedDataScope scope) { ListString conditions new ArrayList(); /* 本部门 */ if (scope.includeSelf()) { conditions.add(deptAlias .dept_id deptId); } /* 向上 */ if (scope.upLevel() 0) { // ancestors 形如0,1,3,10 // 向上 N 级取 ancestors 中倒数 N 位 conditions.add(buildUpDeptSql(deptAlias, deptId, scope.upLevel())); } /* 向下 */ if (scope.downLevel() 0) { if (scope.downLevel() 999) { // 所有子级 conditions.add( deptAlias .dept_id IN ( SELECT dept_id FROM sys_dept WHERE find_in_set( deptId , ancestors) ) ); } else { conditions.add(buildDownDeptSql(deptAlias, deptId, scope.downLevel())); } } return String.join( OR , conditions); } /** * 向上 N 级部门 */ private String buildUpDeptSql(String deptAlias, Long deptId, int upLevel) { // 使用子查询取 ancestors 中的上级 return deptAlias .dept_id IN ( SELECT t.dept_id FROM sys_dept t WHERE t.dept_id IN ( SELECT SUBSTRING_INDEX( SUBSTRING_INDEX(d.ancestors, ,, -( upLevel 1)), ,, 1 ) FROM sys_dept d WHERE d.dept_id deptId ) ); } /** * 向下 N 级部门 */ private String buildDownDeptSql(String deptAlias, Long deptId, int downLevel) { // ancestors 深度控制当前 depth N return deptAlias .dept_id IN ( SELECT d.dept_id FROM sys_dept d WHERE find_in_set( deptId , d.ancestors) AND (LENGTH(d.ancestors) - LENGTH(REPLACE(d.ancestors, ,, ))) ( SELECT (LENGTH(ancestors) - LENGTH(REPLACE(ancestors, ,, ))) downLevel FROM sys_dept WHERE dept_id deptId ) ); } /** * 清空 dataScope防止 SQL 注入 */ private void clearDataScope(JoinPoint joinPoint) { Object params joinPoint.getArgs()[0]; if (params instanceof BaseEntity) { ((BaseEntity) params).getParams().put(DATA_SCOPE, ); } } }五、部门层级 SQL 构建规则1. 本部门d.dept_id {currentDeptId}由includeSelf true控制2. 向上 N 级部门upLevel原理利用ancestors字段从 ancestors 中向前截取 N 个父级示意 SQLd.dept_id IN ( SELECT SUBSTRING_INDEX( SUBSTRING_INDEX(ancestors, ,, -(N 1)), ,, 1 ) FROM sys_dept WHERE dept_id 当前部门ID )3. 向下 N 级部门downLevel3.1 所有子级downLevel 999d.dept_id IN ( SELECT dept_id FROM sys_dept WHERE find_in_set(当前部门ID, ancestors) )3.2 限定层级子部门思路计算 ancestors 的层级深度逗号个数控制最大深度 当前深度 Nd.dept_id IN ( SELECT d.dept_id FROM sys_dept d WHERE find_in_set(当前部门ID, d.ancestors) AND 层级深度(d) 当前层级 N )六、Mapper XML 使用方式select idselectList resultTypexxx SELECT * FROM biz_table t LEFT JOIN sys_dept d ON t.dept_id d.dept_id where 1 1 ${params.dataScope} /where /select说明${params.dataScope}必须保留AOP 动态注入AND ( ... )七、使用示例ExtendedDataScope( deptAlias d, includeSelf true, upLevel 1, downLevel 2 ) public ListSysDept selectDeptList(SysDept dept) { return deptMapper.selectDeptList(dept); }含义说明参数含义includeSelf包含本部门upLevel1包含上一级部门downLevel2包含下两级部门downLevel999包含所有子部门八、方案特点总结✅ 与若依原生 DataScope 解耦✅ 仅依赖部门层级不依赖角色权限✅ 控制粒度细适合复杂组织结构✅ 非侵入式Mapper 无需改动✅ 特别适合安全监管 / GIS / 组织树场景九、可扩展方向后续优化使用 MySQL 8 / PostgreSQL 的WITH RECURSIVE优化层级查询部门层级缓存Redis减少子查询扩展到部门 用户混合数据权限支持多部门归属兼职部门