feat(permission): 添加菜单数据权限功能
- 新增菜单数据规则表和角色菜单数据规则关联表 - 实现菜单数据权限切面和处理器 - 添加数据规则条件和变量枚举 - 实现变量替换工具类和规则构建逻辑 - 在权限分配中集成菜单数据规则关联功能 - 优化部门ID解析逻辑,支持从用户信息中获取默认部门 - 添加菜单组件查询方法和公司访问上下文拦截器改进
This commit is contained in:
617
docs/菜单数据权限使用文档.md
Normal file
617
docs/菜单数据权限使用文档.md
Normal file
File diff suppressed because it is too large
Load Diff
73
sql/dm/20260126菜单数据规则表.sql
Normal file
73
sql/dm/20260126菜单数据规则表.sql
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
-- ----------------------------
|
||||||
|
-- Table structure for system_menu_data_rule
|
||||||
|
-- ----------------------------
|
||||||
|
CREATE TABLE system_menu_data_rule (
|
||||||
|
id bigint NOT NULL PRIMARY KEY,
|
||||||
|
menu_id bigint NOT NULL,
|
||||||
|
rule_name varchar(100) NOT NULL,
|
||||||
|
rule_column varchar(100) DEFAULT NULL NULL,
|
||||||
|
rule_conditions varchar(20) NOT NULL,
|
||||||
|
rule_value varchar(500) NOT NULL,
|
||||||
|
status smallint DEFAULT 1 NOT NULL,
|
||||||
|
sort int DEFAULT 0 NOT NULL,
|
||||||
|
remark varchar(500) DEFAULT NULL NULL,
|
||||||
|
creator varchar(64) DEFAULT '' NULL,
|
||||||
|
create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||||
|
updater varchar(64) DEFAULT '' NULL,
|
||||||
|
update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||||
|
deleted bit DEFAULT '0' NOT NULL,
|
||||||
|
tenant_id bigint DEFAULT 0 NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CREATE INDEX idx_menu_data_rule_menu ON system_menu_data_rule (menu_id);
|
||||||
|
-- CREATE INDEX idx_menu_data_rule_tenant ON system_menu_data_rule (tenant_id);
|
||||||
|
|
||||||
|
COMMENT ON COLUMN system_menu_data_rule.id IS '规则ID';
|
||||||
|
COMMENT ON COLUMN system_menu_data_rule.menu_id IS '菜单ID';
|
||||||
|
COMMENT ON COLUMN system_menu_data_rule.rule_name IS '规则名称';
|
||||||
|
COMMENT ON COLUMN system_menu_data_rule.rule_column IS '规则字段(数据库列名)';
|
||||||
|
COMMENT ON COLUMN system_menu_data_rule.rule_conditions IS '规则条件(=、>、<、IN、LIKE等)';
|
||||||
|
COMMENT ON COLUMN system_menu_data_rule.rule_value IS '规则值(支持变量如#{userId}、#{deptId})';
|
||||||
|
COMMENT ON COLUMN system_menu_data_rule.status IS '状态(0=禁用 1=启用)';
|
||||||
|
COMMENT ON COLUMN system_menu_data_rule.sort IS '排序';
|
||||||
|
COMMENT ON COLUMN system_menu_data_rule.remark IS '备注';
|
||||||
|
COMMENT ON COLUMN system_menu_data_rule.creator IS '创建者';
|
||||||
|
COMMENT ON COLUMN system_menu_data_rule.create_time IS '创建时间';
|
||||||
|
COMMENT ON COLUMN system_menu_data_rule.updater IS '更新者';
|
||||||
|
COMMENT ON COLUMN system_menu_data_rule.update_time IS '更新时间';
|
||||||
|
COMMENT ON COLUMN system_menu_data_rule.deleted IS '是否删除';
|
||||||
|
COMMENT ON COLUMN system_menu_data_rule.tenant_id IS '租户编号';
|
||||||
|
COMMENT ON TABLE system_menu_data_rule IS '菜单数据规则表';
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Table structure for system_role_menu_data_rule
|
||||||
|
-- ----------------------------
|
||||||
|
CREATE TABLE system_role_menu_data_rule (
|
||||||
|
id bigint NOT NULL PRIMARY KEY,
|
||||||
|
role_id bigint NOT NULL,
|
||||||
|
menu_id bigint NOT NULL,
|
||||||
|
data_rule_id bigint NOT NULL,
|
||||||
|
creator varchar(64) DEFAULT '' NULL,
|
||||||
|
create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||||
|
updater varchar(64) DEFAULT '' NULL,
|
||||||
|
update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||||
|
deleted bit DEFAULT '0' NOT NULL,
|
||||||
|
tenant_id bigint DEFAULT 0 NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CREATE INDEX idx_rmdr_role ON system_role_menu_data_rule (role_id);
|
||||||
|
-- CREATE INDEX idx_rmdr_menu ON system_role_menu_data_rule (menu_id);
|
||||||
|
-- CREATE INDEX idx_rmdr_tenant ON system_role_menu_data_rule (tenant_id);
|
||||||
|
-- CREATE INDEX idx_rmdr_role_menu_rule ON system_role_menu_data_rule (role_id, menu_id, data_rule_id);
|
||||||
|
|
||||||
|
COMMENT ON COLUMN system_role_menu_data_rule.id IS '自增主键';
|
||||||
|
COMMENT ON COLUMN system_role_menu_data_rule.role_id IS '角色ID';
|
||||||
|
COMMENT ON COLUMN system_role_menu_data_rule.menu_id IS '菜单ID';
|
||||||
|
COMMENT ON COLUMN system_role_menu_data_rule.data_rule_id IS '数据规则ID';
|
||||||
|
COMMENT ON COLUMN system_role_menu_data_rule.creator IS '创建者';
|
||||||
|
COMMENT ON COLUMN system_role_menu_data_rule.create_time IS '创建时间';
|
||||||
|
COMMENT ON COLUMN system_role_menu_data_rule.updater IS '更新者';
|
||||||
|
COMMENT ON COLUMN system_role_menu_data_rule.update_time IS '更新时间';
|
||||||
|
COMMENT ON COLUMN system_role_menu_data_rule.deleted IS '是否删除';
|
||||||
|
COMMENT ON COLUMN system_role_menu_data_rule.tenant_id IS '租户编号';
|
||||||
|
COMMENT ON TABLE system_role_menu_data_rule IS '角色菜单数据规则关联表';
|
||||||
@@ -4313,3 +4313,74 @@ VALUES
|
|||||||
(5022, 2, '日期格式', 'DATE', 'system_sequence_detail_rule_type', 0, 'success', '', '日期格式规则', 'admin', SYSDATE, 'admin', SYSDATE, 0),
|
(5022, 2, '日期格式', 'DATE', 'system_sequence_detail_rule_type', 0, 'success', '', '日期格式规则', 'admin', SYSDATE, 'admin', SYSDATE, 0),
|
||||||
(5023, 3, '数字格式', 'NUMBER', 'system_sequence_detail_rule_type', 0, 'info', '', '数字格式规则', 'admin', SYSDATE, 'admin', SYSDATE, 0),
|
(5023, 3, '数字格式', 'NUMBER', 'system_sequence_detail_rule_type', 0, 'info', '', '数字格式规则', 'admin', SYSDATE, 'admin', SYSDATE, 0),
|
||||||
(5024, 4, '自定义格式', 'CUSTOM', 'system_sequence_detail_rule_type', 0, 'warning', '', '自定义格式规则', 'admin', SYSDATE, 'admin', SYSDATE, 0);
|
(5024, 4, '自定义格式', 'CUSTOM', 'system_sequence_detail_rule_type', 0, 'warning', '', '自定义格式规则', 'admin', SYSDATE, 'admin', SYSDATE, 0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
增加菜单规则(system_menu_data_rule)和规则角色关联表(system_role_menu_data_rule),同增量脚本:sql/dm/20260126菜单数据规则表.sql
|
||||||
|
*/
|
||||||
|
-- ----------------------------
|
||||||
|
-- Table structure for system_menu_data_rule
|
||||||
|
-- ----------------------------
|
||||||
|
CREATE TABLE system_menu_data_rule (
|
||||||
|
id bigint NOT NULL PRIMARY KEY,
|
||||||
|
menu_id bigint NOT NULL,
|
||||||
|
rule_name varchar(100) NOT NULL,
|
||||||
|
rule_column varchar(100) DEFAULT NULL NULL,
|
||||||
|
rule_conditions varchar(20) NOT NULL,
|
||||||
|
rule_value varchar(500) NOT NULL,
|
||||||
|
status smallint DEFAULT 1 NOT NULL,
|
||||||
|
sort int DEFAULT 0 NOT NULL,
|
||||||
|
remark varchar(500) DEFAULT NULL NULL,
|
||||||
|
creator varchar(64) DEFAULT '' NULL,
|
||||||
|
create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||||
|
updater varchar(64) DEFAULT '' NULL,
|
||||||
|
update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||||
|
deleted bit DEFAULT '0' NOT NULL,
|
||||||
|
tenant_id bigint DEFAULT 0 NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
COMMENT ON COLUMN system_menu_data_rule.id IS '规则ID';
|
||||||
|
COMMENT ON COLUMN system_menu_data_rule.menu_id IS '菜单ID';
|
||||||
|
COMMENT ON COLUMN system_menu_data_rule.rule_name IS '规则名称';
|
||||||
|
COMMENT ON COLUMN system_menu_data_rule.rule_column IS '规则字段(数据库列名)';
|
||||||
|
COMMENT ON COLUMN system_menu_data_rule.rule_conditions IS '规则条件(=、>、<、IN、LIKE等)';
|
||||||
|
COMMENT ON COLUMN system_menu_data_rule.rule_value IS '规则值(支持变量如#{userId}、#{deptId})';
|
||||||
|
COMMENT ON COLUMN system_menu_data_rule.status IS '状态(0=禁用 1=启用)';
|
||||||
|
COMMENT ON COLUMN system_menu_data_rule.sort IS '排序';
|
||||||
|
COMMENT ON COLUMN system_menu_data_rule.remark IS '备注';
|
||||||
|
COMMENT ON COLUMN system_menu_data_rule.creator IS '创建者';
|
||||||
|
COMMENT ON COLUMN system_menu_data_rule.create_time IS '创建时间';
|
||||||
|
COMMENT ON COLUMN system_menu_data_rule.updater IS '更新者';
|
||||||
|
COMMENT ON COLUMN system_menu_data_rule.update_time IS '更新时间';
|
||||||
|
COMMENT ON COLUMN system_menu_data_rule.deleted IS '是否删除';
|
||||||
|
COMMENT ON COLUMN system_menu_data_rule.tenant_id IS '租户编号';
|
||||||
|
COMMENT ON TABLE system_menu_data_rule IS '菜单数据规则表';
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Table structure for system_role_menu_data_rule
|
||||||
|
-- ----------------------------
|
||||||
|
CREATE TABLE system_role_menu_data_rule (
|
||||||
|
id bigint NOT NULL PRIMARY KEY,
|
||||||
|
role_id bigint NOT NULL,
|
||||||
|
menu_id bigint NOT NULL,
|
||||||
|
data_rule_id bigint NOT NULL,
|
||||||
|
creator varchar(64) DEFAULT '' NULL,
|
||||||
|
create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||||
|
updater varchar(64) DEFAULT '' NULL,
|
||||||
|
update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||||
|
deleted bit DEFAULT '0' NOT NULL,
|
||||||
|
tenant_id bigint DEFAULT 0 NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
COMMENT ON COLUMN system_role_menu_data_rule.id IS '自增主键';
|
||||||
|
COMMENT ON COLUMN system_role_menu_data_rule.role_id IS '角色ID';
|
||||||
|
COMMENT ON COLUMN system_role_menu_data_rule.menu_id IS '菜单ID';
|
||||||
|
COMMENT ON COLUMN system_role_menu_data_rule.data_rule_id IS '数据规则ID';
|
||||||
|
COMMENT ON COLUMN system_role_menu_data_rule.creator IS '创建者';
|
||||||
|
COMMENT ON COLUMN system_role_menu_data_rule.create_time IS '创建时间';
|
||||||
|
COMMENT ON COLUMN system_role_menu_data_rule.updater IS '更新者';
|
||||||
|
COMMENT ON COLUMN system_role_menu_data_rule.update_time IS '更新时间';
|
||||||
|
COMMENT ON COLUMN system_role_menu_data_rule.deleted IS '是否删除';
|
||||||
|
COMMENT ON COLUMN system_role_menu_data_rule.tenant_id IS '租户编号';
|
||||||
|
COMMENT ON TABLE system_role_menu_data_rule IS '角色菜单数据规则关联表';
|
||||||
|
|||||||
@@ -4,12 +4,15 @@ import com.zt.plat.framework.datapermission.core.aop.CompanyDataPermissionIgnore
|
|||||||
import com.zt.plat.framework.datapermission.core.aop.DataPermissionAnnotationAdvisor;
|
import com.zt.plat.framework.datapermission.core.aop.DataPermissionAnnotationAdvisor;
|
||||||
import com.zt.plat.framework.datapermission.core.aop.DeptDataPermissionIgnoreAspect;
|
import com.zt.plat.framework.datapermission.core.aop.DeptDataPermissionIgnoreAspect;
|
||||||
import com.zt.plat.framework.datapermission.core.db.DataPermissionRuleHandler;
|
import com.zt.plat.framework.datapermission.core.db.DataPermissionRuleHandler;
|
||||||
|
import com.zt.plat.framework.datapermission.core.menudatapermission.aop.MenuDataPermissionAspect;
|
||||||
|
import com.zt.plat.framework.datapermission.core.menudatapermission.handler.MenuDataPermissionHandler;
|
||||||
import com.zt.plat.framework.datapermission.core.rule.DataPermissionRule;
|
import com.zt.plat.framework.datapermission.core.rule.DataPermissionRule;
|
||||||
import com.zt.plat.framework.datapermission.core.rule.DataPermissionRuleFactory;
|
import com.zt.plat.framework.datapermission.core.rule.DataPermissionRuleFactory;
|
||||||
import com.zt.plat.framework.datapermission.core.rule.DataPermissionRuleFactoryImpl;
|
import com.zt.plat.framework.datapermission.core.rule.DataPermissionRuleFactoryImpl;
|
||||||
import com.zt.plat.framework.mybatis.core.util.MyBatisUtils;
|
import com.zt.plat.framework.mybatis.core.util.MyBatisUtils;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.inner.DataPermissionInterceptor;
|
import com.baomidou.mybatisplus.extension.plugins.inner.DataPermissionInterceptor;
|
||||||
|
import org.mybatis.spring.annotation.MapperScan;
|
||||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
|
|
||||||
@@ -21,6 +24,7 @@ import java.util.List;
|
|||||||
* @author ZT
|
* @author ZT
|
||||||
*/
|
*/
|
||||||
@AutoConfiguration
|
@AutoConfiguration
|
||||||
|
@MapperScan("com.zt.plat.framework.datapermission.core.menudatapermission.dal.mapper")
|
||||||
public class ZtDataPermissionAutoConfiguration {
|
public class ZtDataPermissionAutoConfiguration {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@@ -40,6 +44,21 @@ public class ZtDataPermissionAutoConfiguration {
|
|||||||
return handler;
|
return handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public MenuDataPermissionHandler menuDataPermissionHandler(MybatisPlusInterceptor interceptor) {
|
||||||
|
// 创建菜单数据权限处理器
|
||||||
|
MenuDataPermissionHandler handler = new MenuDataPermissionHandler();
|
||||||
|
DataPermissionInterceptor inner = new DataPermissionInterceptor(handler);
|
||||||
|
// 添加到 interceptor 中,放在部门数据权限之后
|
||||||
|
MyBatisUtils.addInterceptor(interceptor, inner, 1);
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public MenuDataPermissionAspect menuDataPermissionAspect() {
|
||||||
|
return new MenuDataPermissionAspect();
|
||||||
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public DataPermissionAnnotationAdvisor dataPermissionAnnotationAdvisor() {
|
public DataPermissionAnnotationAdvisor dataPermissionAnnotationAdvisor() {
|
||||||
return new DataPermissionAnnotationAdvisor();
|
return new DataPermissionAnnotationAdvisor();
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package com.zt.plat.framework.datapermission.core.menudatapermission.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据权限注解
|
||||||
|
* 标注在Controller方法上,表示该方法需要应用菜单数据规则
|
||||||
|
*
|
||||||
|
* 参考JeecgBoot的实现方式
|
||||||
|
*
|
||||||
|
* @author ZT
|
||||||
|
*/
|
||||||
|
@Target({ElementType.METHOD})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Documented
|
||||||
|
public @interface PermissionData {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 页面组件路径
|
||||||
|
* 用于匹配菜单表中的component字段
|
||||||
|
* 例如:system/role 对应角色管理菜单
|
||||||
|
*/
|
||||||
|
String pageComponent();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否启用
|
||||||
|
*/
|
||||||
|
boolean enable() default true;
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
package com.zt.plat.framework.datapermission.core.menudatapermission.aop;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import com.zt.plat.framework.datapermission.core.menudatapermission.annotation.PermissionData;
|
||||||
|
import com.zt.plat.framework.datapermission.core.menudatapermission.context.MenuDataRuleContextHolder;
|
||||||
|
import com.zt.plat.framework.datapermission.core.menudatapermission.model.MenuDataRuleDTO;
|
||||||
|
import com.zt.plat.framework.datapermission.core.menudatapermission.service.MenuDataRuleLoader;
|
||||||
|
import com.zt.plat.framework.security.core.util.SecurityFrameworkUtils;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.aspectj.lang.ProceedingJoinPoint;
|
||||||
|
import org.aspectj.lang.annotation.Around;
|
||||||
|
import org.aspectj.lang.annotation.Aspect;
|
||||||
|
import org.aspectj.lang.reflect.MethodSignature;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 菜单数据权限切面
|
||||||
|
* 拦截 @PermissionData 注解的方法,加载并应用菜单数据规则
|
||||||
|
*
|
||||||
|
* @author ZT
|
||||||
|
*/
|
||||||
|
@Aspect
|
||||||
|
@Component
|
||||||
|
@Slf4j
|
||||||
|
public class MenuDataPermissionAspect {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private MenuDataRuleLoader menuDataRuleLoader;
|
||||||
|
|
||||||
|
@Around("@annotation(com.zt.plat.framework.datapermission.core.menudatapermission.annotation.PermissionData)")
|
||||||
|
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
|
||||||
|
// 获取方法签名
|
||||||
|
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
||||||
|
Method method = signature.getMethod();
|
||||||
|
PermissionData annotation = method.getAnnotation(PermissionData.class);
|
||||||
|
|
||||||
|
// 如果未启用,直接执行
|
||||||
|
if (!annotation.enable()) {
|
||||||
|
return joinPoint.proceed();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 获取当前用户ID
|
||||||
|
Long userId = SecurityFrameworkUtils.getLoginUserId();
|
||||||
|
if (userId == null) {
|
||||||
|
log.debug("[MenuDataPermissionAspect][未登录,跳过菜单数据权限]");
|
||||||
|
return joinPoint.proceed();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从注解获取pageComponent
|
||||||
|
String pageComponent = annotation.pageComponent();
|
||||||
|
if (pageComponent == null || pageComponent.isEmpty()) {
|
||||||
|
log.warn("[MenuDataPermissionAspect][未指定pageComponent,跳过菜单数据权限]");
|
||||||
|
return joinPoint.proceed();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据pageComponent查询菜单ID
|
||||||
|
Long menuId = menuDataRuleLoader.getMenuIdByPageComponent(pageComponent);
|
||||||
|
if (menuId == null) {
|
||||||
|
log.warn("[MenuDataPermissionAspect][未找到匹配的菜单: {},跳过菜单数据权限]", pageComponent);
|
||||||
|
return joinPoint.proceed();
|
||||||
|
}
|
||||||
|
|
||||||
|
log.debug("[MenuDataPermissionAspect][pageComponent: {} 对应菜单ID: {}]", pageComponent, menuId);
|
||||||
|
|
||||||
|
// 加载用户的菜单数据规则
|
||||||
|
List<MenuDataRuleDTO> rules = menuDataRuleLoader.getUserMenuDataRules(userId, menuId);
|
||||||
|
|
||||||
|
if (CollUtil.isEmpty(rules)) {
|
||||||
|
log.debug("[MenuDataPermissionAspect][用户 {} 在菜单 {} 下无数据规则]", userId, menuId);
|
||||||
|
} else {
|
||||||
|
log.debug("[MenuDataPermissionAspect][用户 {} 在菜单 {} 下加载了 {} 条数据规则]",
|
||||||
|
userId, menuId, rules.size());
|
||||||
|
// 将规则存入 ThreadLocal
|
||||||
|
MenuDataRuleContextHolder.setRules(rules);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行目标方法
|
||||||
|
return joinPoint.proceed();
|
||||||
|
} finally {
|
||||||
|
// 清理 ThreadLocal
|
||||||
|
MenuDataRuleContextHolder.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package com.zt.plat.framework.datapermission.core.menudatapermission.config;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.inner.DataPermissionInterceptor;
|
||||||
|
import com.zt.plat.framework.datapermission.core.menudatapermission.handler.MenuDataPermissionHandler;
|
||||||
|
import com.zt.plat.framework.mybatis.core.util.MyBatisUtils;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.ComponentScan;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 菜单数据权限配置类
|
||||||
|
*
|
||||||
|
* @author ZT
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@ComponentScan("com.zt.plat.framework.datapermission.core.menudatapermission")
|
||||||
|
public class MenuDataPermissionConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnBean(MybatisPlusInterceptor.class)
|
||||||
|
public MenuDataPermissionHandler menuDataPermissionHandler(MybatisPlusInterceptor interceptor) {
|
||||||
|
// 创建菜单数据权限处理器
|
||||||
|
MenuDataPermissionHandler handler = new MenuDataPermissionHandler();
|
||||||
|
|
||||||
|
// 创建 DataPermissionInterceptor 拦截器
|
||||||
|
DataPermissionInterceptor inner = new DataPermissionInterceptor(handler);
|
||||||
|
|
||||||
|
// 添加到 interceptor 中
|
||||||
|
// 添加在索引1的位置,在部门数据权限之后,但在分页插件之前
|
||||||
|
MyBatisUtils.addInterceptor(interceptor, inner, 1);
|
||||||
|
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package com.zt.plat.framework.datapermission.core.menudatapermission.context;
|
||||||
|
|
||||||
|
import com.zt.plat.framework.datapermission.core.menudatapermission.model.MenuDataRuleDTO;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 菜单数据规则上下文持有者
|
||||||
|
* 使用 ThreadLocal 存储当前请求的菜单数据规则
|
||||||
|
*
|
||||||
|
* @author ZT
|
||||||
|
*/
|
||||||
|
public class MenuDataRuleContextHolder {
|
||||||
|
|
||||||
|
private static final ThreadLocal<List<MenuDataRuleDTO>> CONTEXT = new ThreadLocal<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置当前请求的菜单数据规则
|
||||||
|
*
|
||||||
|
* @param rules 规则列表
|
||||||
|
*/
|
||||||
|
public static void setRules(List<MenuDataRuleDTO> rules) {
|
||||||
|
CONTEXT.set(rules);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前请求的菜单数据规则
|
||||||
|
*
|
||||||
|
* @return 规则列表
|
||||||
|
*/
|
||||||
|
public static List<MenuDataRuleDTO> getRules() {
|
||||||
|
return CONTEXT.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除当前请求的菜单数据规则
|
||||||
|
*/
|
||||||
|
public static void clear() {
|
||||||
|
CONTEXT.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package com.zt.plat.framework.datapermission.core.menudatapermission.dal.mapper;
|
||||||
|
|
||||||
|
import com.zt.plat.framework.datapermission.core.menudatapermission.model.MenuDataRuleDTO;
|
||||||
|
import com.zt.plat.framework.tenant.core.aop.TenantIgnore;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
import org.apache.ibatis.annotations.Select;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 菜单数据权限 Mapper
|
||||||
|
* 用于查询菜单和菜单数据规则
|
||||||
|
*
|
||||||
|
* @author ZT
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface MenuDataPermissionMapper {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据页面组件路径获取菜单ID
|
||||||
|
*
|
||||||
|
* @param component 页面组件路径,如:system/role/index
|
||||||
|
* @return 菜单ID,如果未找到返回null
|
||||||
|
*/
|
||||||
|
@Select("SELECT id FROM system_menu WHERE component = #{component} AND deleted = 0 LIMIT 1")
|
||||||
|
Long selectMenuIdByComponent(@Param("component") String component);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户在指定菜单下的有效数据规则
|
||||||
|
*
|
||||||
|
* @param userId 用户ID
|
||||||
|
* @param menuId 菜单ID
|
||||||
|
* @return 数据规则列表
|
||||||
|
*/
|
||||||
|
@TenantIgnore
|
||||||
|
@Select("<script>" +
|
||||||
|
"SELECT mdr.rule_column, mdr.rule_conditions, mdr.rule_value, mdr.status " +
|
||||||
|
"FROM system_menu_data_rule mdr " +
|
||||||
|
"INNER JOIN system_role_menu_data_rule rmdr ON mdr.id = rmdr.data_rule_id " +
|
||||||
|
"INNER JOIN system_user_role ur ON rmdr.role_id = ur.role_id " +
|
||||||
|
"WHERE mdr.menu_id = #{menuId} " +
|
||||||
|
"AND ur.user_id = #{userId} " +
|
||||||
|
"AND mdr.status = 1 " +
|
||||||
|
"AND mdr.deleted = 0 " +
|
||||||
|
"AND rmdr.deleted = 0 " +
|
||||||
|
"AND ur.deleted = 0" +
|
||||||
|
"</script>")
|
||||||
|
List<MenuDataRuleDTO> selectUserMenuDataRules(@Param("userId") Long userId,
|
||||||
|
@Param("menuId") Long menuId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package com.zt.plat.framework.datapermission.core.menudatapermission.handler;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.handler.MultiDataPermissionHandler;
|
||||||
|
import com.zt.plat.framework.datapermission.core.menudatapermission.util.MenuDataPermissionRule;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import net.sf.jsqlparser.JSQLParserException;
|
||||||
|
import net.sf.jsqlparser.expression.Expression;
|
||||||
|
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
|
||||||
|
import net.sf.jsqlparser.schema.Table;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 菜单数据权限处理器
|
||||||
|
* 基于 MyBatis Plus 的数据权限插件,应用菜单数据规则到 SQL 查询
|
||||||
|
*
|
||||||
|
* @author ZT
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class MenuDataPermissionHandler implements MultiDataPermissionHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Expression getSqlSegment(Table table, Expression where, String mappedStatementId) {
|
||||||
|
try {
|
||||||
|
// 从 ThreadLocal 获取菜单数据规则并构建 SQL 条件
|
||||||
|
String sqlCondition = MenuDataPermissionRule.buildSqlCondition();
|
||||||
|
|
||||||
|
if (StrUtil.isBlank(sqlCondition)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将 SQL 字符串解析为 Expression 对象
|
||||||
|
Expression expression = CCJSqlParserUtil.parseCondExpression(sqlCondition);
|
||||||
|
log.debug("[MenuDataPermissionHandler][表: {}, 添加条件: {}]", table.getName(), sqlCondition);
|
||||||
|
return expression;
|
||||||
|
|
||||||
|
} catch (JSQLParserException e) {
|
||||||
|
log.error("[MenuDataPermissionHandler][解析 SQL 条件失败]", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user