1. 新增 dept 类型的全局上下文权限
This commit is contained in:
@@ -3,6 +3,7 @@ package com.zt.plat.framework.datapermission.core.rule.dept;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
|
||||
import com.zt.plat.framework.common.biz.system.permission.PermissionCommonApi;
|
||||
import com.zt.plat.framework.common.biz.system.permission.dto.DeptDataPermissionRespDTO;
|
||||
import com.zt.plat.framework.common.enums.UserTypeEnum;
|
||||
@@ -14,7 +15,7 @@ import com.zt.plat.framework.mybatis.core.util.MyBatisUtils;
|
||||
import com.zt.plat.framework.security.core.LoginUser;
|
||||
import com.zt.plat.framework.security.core.util.SecurityFrameworkUtils;
|
||||
import com.zt.plat.framework.tenant.core.context.CompanyContextHolder;
|
||||
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
|
||||
import com.zt.plat.framework.tenant.core.context.DeptContextHolder;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.sf.jsqlparser.expression.Alias;
|
||||
@@ -108,6 +109,11 @@ public class DeptDataPermissionRule implements DataPermissionRule {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 显式忽略部门数据权限时直接放行
|
||||
if (DeptContextHolder.shouldIgnore()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 获得数据权限
|
||||
DeptDataPermissionRespDTO deptDataPermission = loginUser.getContext(CONTEXT_KEY, DeptDataPermissionRespDTO.class);
|
||||
// 从上下文中拿不到,则调用逻辑进行获取
|
||||
@@ -136,6 +142,20 @@ public class DeptDataPermissionRule implements DataPermissionRule {
|
||||
}
|
||||
}
|
||||
|
||||
// 若存在部门上下文,优先使用上下文中的单一部门,必要时校验公司一致性
|
||||
Long ctxDeptId = DeptContextHolder.getDeptId();
|
||||
if (ctxDeptId != null && ctxDeptId > 0L) {
|
||||
Long currentCompanyId = CompanyContextHolder.getCompanyId();
|
||||
Long ctxCompanyId = DeptContextHolder.getCompanyId();
|
||||
Long compareCompanyId = ctxCompanyId != null ? ctxCompanyId : currentCompanyId;
|
||||
if (currentCompanyId != null && currentCompanyId > 0L
|
||||
&& compareCompanyId != null && !currentCompanyId.equals(compareCompanyId)) {
|
||||
log.warn("[getExpression][LoginUser({}) Table({}/{}) DeptContextHolder company mismatch: currentCompanyId={}, ctxCompanyId={}, ctxDeptId={}, source=DeptContextHolder]",
|
||||
JsonUtils.toJsonString(loginUser), tableName, tableAlias == null ? null : tableAlias.getName(),
|
||||
currentCompanyId, compareCompanyId, ctxDeptId);
|
||||
}
|
||||
}
|
||||
|
||||
// 情况一,如果是 ALL 可查看全部,则无需拼接条件
|
||||
if (deptDataPermission.getAll()) {
|
||||
return null;
|
||||
|
||||
@@ -7,10 +7,13 @@ import com.zt.plat.framework.common.enums.UserTypeEnum;
|
||||
import com.zt.plat.framework.common.util.collection.SetUtils;
|
||||
import com.zt.plat.framework.security.core.LoginUser;
|
||||
import com.zt.plat.framework.security.core.util.SecurityFrameworkUtils;
|
||||
import com.zt.plat.framework.tenant.core.context.CompanyContextHolder;
|
||||
import com.zt.plat.framework.tenant.core.context.DeptContextHolder;
|
||||
import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest;
|
||||
import com.zt.plat.framework.common.biz.system.permission.dto.DeptDataPermissionRespDTO;
|
||||
import net.sf.jsqlparser.expression.Alias;
|
||||
import net.sf.jsqlparser.expression.Expression;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
@@ -27,6 +30,7 @@ import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.same;
|
||||
import static org.mockito.Mockito.mockStatic;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
@@ -48,7 +52,13 @@ class DeptDataPermissionRuleTest extends BaseMockitoUnitTest {
|
||||
// 清空 rule
|
||||
rule.getTableNames().clear();
|
||||
((Map<String, String>) ReflectUtil.getFieldValue(rule, "deptColumns")).clear();
|
||||
((Map<String, String>) ReflectUtil.getFieldValue(rule, "deptColumns")).clear();
|
||||
((Map<String, String>) ReflectUtil.getFieldValue(rule, "userColumns")).clear();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
DeptContextHolder.clear();
|
||||
CompanyContextHolder.clear();
|
||||
}
|
||||
|
||||
@Test // 无 LoginUser
|
||||
@@ -236,4 +246,88 @@ class DeptDataPermissionRuleTest extends BaseMockitoUnitTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test // 忽略部门数据权限,直接放行
|
||||
void testGetExpression_ignoreDeptContext() {
|
||||
try (MockedStatic<SecurityFrameworkUtils> secMock = mockStatic(SecurityFrameworkUtils.class);
|
||||
MockedStatic<DeptContextHolder> deptCtxMock = mockStatic(DeptContextHolder.class)) {
|
||||
String tableName = "t_order";
|
||||
Alias alias = new Alias("o");
|
||||
LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L)
|
||||
.setUserType(UserTypeEnum.ADMIN.getValue()));
|
||||
secMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser);
|
||||
deptCtxMock.when(DeptContextHolder::shouldIgnore).thenReturn(true);
|
||||
|
||||
Expression expression = rule.getExpression(tableName, alias);
|
||||
|
||||
assertNull(expression);
|
||||
verifyNoInteractions(permissionApi);
|
||||
}
|
||||
}
|
||||
|
||||
@Test // 上下文部门存在且公司一致时,清空原集合并覆盖为单一 deptId
|
||||
void testGetExpression_deptContextOverride_companyMatch() {
|
||||
try (MockedStatic<SecurityFrameworkUtils> secMock = mockStatic(SecurityFrameworkUtils.class);
|
||||
MockedStatic<DeptContextHolder> deptCtxMock = mockStatic(DeptContextHolder.class);
|
||||
MockedStatic<CompanyContextHolder> companyCtxMock = mockStatic(CompanyContextHolder.class)) {
|
||||
|
||||
String tableName = "t_user";
|
||||
Alias tableAlias = new Alias("u");
|
||||
LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L)
|
||||
.setUserType(UserTypeEnum.ADMIN.getValue()));
|
||||
secMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser);
|
||||
|
||||
DeptDataPermissionRespDTO deptDataPermission = new DeptDataPermissionRespDTO()
|
||||
.setDeptIds(CollUtil.newLinkedHashSet(10L, 20L))
|
||||
.setCompanyId(1L);
|
||||
when(permissionApi.getDeptDataPermission(same(1L))).thenReturn(success(deptDataPermission));
|
||||
|
||||
deptCtxMock.when(DeptContextHolder::shouldIgnore).thenReturn(false);
|
||||
deptCtxMock.when(DeptContextHolder::getDeptId).thenReturn(99L);
|
||||
deptCtxMock.when(DeptContextHolder::getCompanyId).thenReturn(1L);
|
||||
companyCtxMock.when(CompanyContextHolder::getCompanyId).thenReturn(1L);
|
||||
companyCtxMock.when(CompanyContextHolder::isIgnore).thenReturn(false);
|
||||
|
||||
rule.addDeptColumn(tableName, "dept_id");
|
||||
|
||||
Expression expression = rule.getExpression(tableName, tableAlias);
|
||||
|
||||
assertEquals("u.dept_id IN (99)", expression.toString());
|
||||
assertEquals(CollUtil.newLinkedHashSet(99L), deptDataPermission.getDeptIds());
|
||||
assertEquals(1L, deptDataPermission.getCompanyId());
|
||||
}
|
||||
}
|
||||
|
||||
@Test // 上下文部门存在但公司不一致时,记录告警并保持原逻辑(不覆盖)
|
||||
void testGetExpression_deptContextOverride_companyMismatch() {
|
||||
try (MockedStatic<SecurityFrameworkUtils> secMock = mockStatic(SecurityFrameworkUtils.class);
|
||||
MockedStatic<DeptContextHolder> deptCtxMock = mockStatic(DeptContextHolder.class);
|
||||
MockedStatic<CompanyContextHolder> companyCtxMock = mockStatic(CompanyContextHolder.class)) {
|
||||
|
||||
String tableName = "t_user";
|
||||
Alias tableAlias = new Alias("u");
|
||||
LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L)
|
||||
.setUserType(UserTypeEnum.ADMIN.getValue()));
|
||||
secMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser);
|
||||
|
||||
DeptDataPermissionRespDTO deptDataPermission = new DeptDataPermissionRespDTO()
|
||||
.setDeptIds(CollUtil.newLinkedHashSet(10L))
|
||||
.setCompanyId(1L);
|
||||
when(permissionApi.getDeptDataPermission(same(1L))).thenReturn(success(deptDataPermission));
|
||||
|
||||
deptCtxMock.when(DeptContextHolder::shouldIgnore).thenReturn(false);
|
||||
deptCtxMock.when(DeptContextHolder::getDeptId).thenReturn(99L);
|
||||
deptCtxMock.when(DeptContextHolder::getCompanyId).thenReturn(2L);
|
||||
companyCtxMock.when(CompanyContextHolder::getCompanyId).thenReturn(1L);
|
||||
companyCtxMock.when(CompanyContextHolder::isIgnore).thenReturn(false);
|
||||
|
||||
rule.addDeptColumn(tableName, "dept_id");
|
||||
|
||||
Expression expression = rule.getExpression(tableName, tableAlias);
|
||||
|
||||
assertEquals("u.dept_id IN (10)", expression.toString());
|
||||
assertEquals(CollUtil.newLinkedHashSet(10L), deptDataPermission.getDeptIds());
|
||||
assertEquals(1L, deptDataPermission.getCompanyId());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user