feat:(REQ-2300) 增加数据库查询数据权限sdk

This commit is contained in:
lilong 2024-06-05 17:01:06 +08:00
parent e76f07798d
commit 2323e7a2a0
21 changed files with 873 additions and 54 deletions

View File

@ -1,5 +1,6 @@
package cn.axzo.framework.datapermission.annotation; package cn.axzo.framework.datapermission.annotation;
import cn.axzo.framework.datapermission.rule.OrgDefaultRule;
import cn.axzo.framework.datapermission.rule.DataPermissionRule; import cn.axzo.framework.datapermission.rule.DataPermissionRule;
/** /**
@ -8,9 +9,9 @@ import cn.axzo.framework.datapermission.rule.DataPermissionRule;
* @date 2024/5/30 17:57 * @date 2024/5/30 17:57
*/ */
public @interface DataPermission { public @interface DataPermission {
boolean enable() default false; boolean enable() default true;
Class<? extends DataPermissionRule>[] includeRule() default {}; Class<? extends DataPermissionRule> includeRule() default OrgDefaultRule.class;
String bizCode() default ""; String bizCode() default "";

View File

@ -1,16 +1,61 @@
package cn.axzo.framework.datapermission.aop; package cn.axzo.framework.datapermission.aop;
import cn.axzo.framework.auth.domain.ContextInfo;
import cn.axzo.framework.auth.domain.ContextInfoHolder;
import cn.axzo.framework.datapermission.annotation.DataPermission; import cn.axzo.framework.datapermission.annotation.DataPermission;
import cn.axzo.framework.datapermission.config.DataPermissionConfig; import cn.axzo.framework.datapermission.context.DataPermissionContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.BooleanUtils;
import org.springframework.core.annotation.Order;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/** /**
* *
* @author tanjie@axzo.cn * @author tanjie@axzo.cn
* @date 2024/5/31 16:37 * @date 2024/5/31 16:37
*/ */
public class DataPermissionFilter { @Order(2)
@Slf4j
public class DataPermissionFilter implements HandlerInterceptor {
// 拦截@DataPermission /**
// 设置DataPermissionContext * 拦截@DataPermission
* 设置DataPermissionContext
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
DataPermission dataPermission = handlerMethod.getMethodAnnotation(DataPermission.class);
if (dataPermission == null || BooleanUtils.isNotTrue(dataPermission.enable())) {
return true;
}
DataPermissionContextHolder.DataPermissionContext dataPermissionContext = DataPermissionContextHolder.DataPermissionContext.builder()
.dataPermission(dataPermission)
.build();
// 有数据权限注解但没用户信息让后面拦截器做处理更合理(可以返回空数据或者不阻塞业务正常查询数据)
ContextInfo contextInfo = ContextInfoHolder.get();
if (contextInfo != null) {
dataPermissionContext.setWorkspaceId(contextInfo.getWorkspaceId());
dataPermissionContext.setOuId(contextInfo.getOuId());
dataPermissionContext.setPersonId(contextInfo.getUserInfo().getPersonId());
}
DataPermissionContextHolder.setContext(dataPermissionContext);
}
return true;
}
} }

View File

@ -4,6 +4,8 @@ import cn.axzo.framework.datapermission.annotation.DataPermission;
import cn.axzo.framework.datapermission.context.DataPermissionContextFactory; import cn.axzo.framework.datapermission.context.DataPermissionContextFactory;
import cn.axzo.framework.datapermission.context.DataPermissionContextHolder; import cn.axzo.framework.datapermission.context.DataPermissionContextHolder;
import cn.axzo.framework.datapermission.rule.DataPermissionRule; import cn.axzo.framework.datapermission.rule.DataPermissionRule;
import cn.axzo.karma.client.feign.tyr.response.MatchDataObjectResp;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.parser.SqlParserHelper; import com.baomidou.mybatisplus.core.parser.SqlParserHelper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils; import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
@ -52,9 +54,10 @@ import java.util.Optional;
* @date 2024/5/31 11:41 * @date 2024/5/31 11:41
*/ */
@RequiredArgsConstructor @RequiredArgsConstructor
public class DataPermissionMybatisInterceptor extends JsqlParserSupport implements InnerInterceptor { public class DataPermissionMybatisInterceptor extends JsqlParserSupport implements InnerInterceptor {
private final DataPermissionContextFactory dataPermissionContextFactory; private final DataPermissionContextFactory dataPermissionContextFactory;
@Override @Override
public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
if (!filter()) { if (!filter()) {
@ -315,7 +318,9 @@ public class DataPermissionMybatisInterceptor extends JsqlParserSupport implem
private boolean filter() { private boolean filter() {
DataPermissionContextHolder.DataPermissionContext dataPermissionContext = DataPermissionContextHolder.get(); DataPermissionContextHolder.DataPermissionContext dataPermissionContext = DataPermissionContextHolder.get();
if (null == dataPermissionContext|| !dataPermissionContext.getDataPermission().enable()) { if (null == dataPermissionContext
|| dataPermissionContext.getDataPermission() == null
|| !dataPermissionContext.getDataPermission().enable()) {
return false; return false;
} }

View File

@ -2,15 +2,11 @@ package cn.axzo.framework.datapermission.config;
import cn.axzo.framework.datapermission.aop.DataPermissionMybatisInterceptor; import cn.axzo.framework.datapermission.aop.DataPermissionMybatisInterceptor;
import cn.axzo.framework.datapermission.context.DataPermissionContextFactory; import cn.axzo.framework.datapermission.context.DataPermissionContextFactory;
import cn.axzo.framework.datapermission.rule.DataPermissionDefaultRule;
import cn.axzo.framework.datapermission.rule.DataPermissionRule; import cn.axzo.framework.datapermission.rule.DataPermissionRule;
import cn.axzo.karma.client.feign.FlowSupportApi;
import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import java.net.DatagramSocketImplFactory;
import java.util.List; import java.util.List;
/** /**
@ -20,12 +16,6 @@ import java.util.List;
@ConditionalOnBean(MybatisPlusInterceptor.class) @ConditionalOnBean(MybatisPlusInterceptor.class)
public class DataPermissionConfig { public class DataPermissionConfig {
@Bean
public DataPermissionDefaultRule deptDataPermissionRule(FlowSupportApi permissionApi) {
// 创建 DeptDataPermissionRule 对象
return new DataPermissionDefaultRule(permissionApi);
}
@Bean @Bean
@ConditionalOnBean(MybatisPlusInterceptor.class) @ConditionalOnBean(MybatisPlusInterceptor.class)
public DataPermissionMybatisInterceptor mybatisDataPermissionInterceptor(MybatisPlusInterceptor mybatisPlusInterceptor, DataPermissionContextFactory dataPermissionContextFactory) { public DataPermissionMybatisInterceptor mybatisDataPermissionInterceptor(MybatisPlusInterceptor mybatisPlusInterceptor, DataPermissionContextFactory dataPermissionContextFactory) {

View File

@ -4,8 +4,8 @@ import cn.axzo.framework.datapermission.annotation.DataPermission;
import cn.axzo.framework.datapermission.rule.DataPermissionRule; import cn.axzo.framework.datapermission.rule.DataPermissionRule;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@ -23,8 +23,8 @@ public class DataPermissionContextFactory {
} }
public List<DataPermissionRule> getRuleByDataPermission(DataPermission dataPermission) { public List<DataPermissionRule> getRuleByDataPermission(DataPermission dataPermission) {
Class<? extends DataPermissionRule>[] classes = dataPermission.includeRule(); Class<? extends DataPermissionRule> classes = dataPermission.includeRule();
return rules.stream().filter(rule -> Arrays.stream(classes).anyMatch(e -> e.equals(rule.getClass()))).collect(Collectors.toList()); return rules.stream().filter(rule -> Objects.equals(classes, rule.getClass())).collect(Collectors.toList());
} }
} }

View File

@ -2,7 +2,10 @@ package cn.axzo.framework.datapermission.context;
import cn.axzo.framework.datapermission.annotation.DataPermission; import cn.axzo.framework.datapermission.annotation.DataPermission;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Map; import java.util.Map;
@ -16,6 +19,15 @@ public class DataPermissionContextHolder {
private final static ThreadLocal<DataPermissionContext> DATA_PERMISSION_CONTEXT = new ThreadLocal<>(); private final static ThreadLocal<DataPermissionContext> DATA_PERMISSION_CONTEXT = new ThreadLocal<>();
public static final String SELF_SUBORDINATE = "SELF_SUBORDINATE";
public static final String DEPARTMENT_ONLY = "DEPARTMENT_ONLY";
public static final String DEPARTMENT_SUBORDINATE = "DEPARTMENT_SUBORDINATE";
public static final String UNIT_ONLY = "UNIT_ONLY";
public static final String UNIT_ONLY_EXCLUDE_TEAM = "UNIT_ONLY_EXCLUDE_TEAM";
public static final String UNIT_DIRECT_SUBORDINATE = "UNIT_DIRECT_SUBORDINATE";
public static final String UNIT_COOPERATE_SUBORDINATE = "UNIT_COOPERATE_SUBORDINATE";
public static final String UNIT_ALL_SUBORDINATE = "UNIT_ALL_SUBORDINATE";
public static final String WORKSPACE = "WORKSPACE";
public static void setContext(DataPermissionContext dataPermission) { public static void setContext(DataPermissionContext dataPermission) {
DATA_PERMISSION_CONTEXT.set(dataPermission); DATA_PERMISSION_CONTEXT.set(dataPermission);
@ -23,6 +35,9 @@ public class DataPermissionContextHolder {
@Data @Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class DataPermissionContext { public static class DataPermissionContext {
private DataPermission dataPermission; private DataPermission dataPermission;
private Long ouId; private Long ouId;
@ -48,6 +63,10 @@ public class DataPermissionContextHolder {
* *
*/ */
private Map<String, Object> permissionDataCache = Maps.newConcurrentMap(); private Map<String, Object> permissionDataCache = Maps.newConcurrentMap();
public void putPermissionData(String key, Object value) {
permissionDataCache.put(key, value);
}
} }
public static void remove() { public static void remove() {

View File

@ -0,0 +1,65 @@
package cn.axzo.framework.datapermission.dataObject;
import cn.axzo.karma.client.feign.tyr.response.MatchDataObjectResp;
import com.google.common.collect.Sets;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.util.CollectionUtils;
import java.util.List;
import java.util.Objects;
import java.util.Set;
public interface DataObjectRule {
/**
* 解析规则返回解析后的数据
* @param context
* @return
*/
void resolve(DataObjectRuleContext context);
/**
* 查询规则的id
* @return
*/
Integer getRulePermissionId();
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
class DataObjectRuleContext {
/**
* 解析后的人员id
*/
@Builder.Default
private Set<Long> personIds = Sets.newHashSet();
/**
* 解析后的部门id
*/
@Builder.Default
private Set<Long> nodeIds = Sets.newHashSet();
public void addPersonIds(Set<Long> personIds) {
this.getPersonIds().addAll(personIds);
}
public void addNodeIds(Set<Long> nodeIds) {
this.getNodeIds().addAll(nodeIds);
}
}
default boolean match(List<MatchDataObjectResp.DataObjectRule> rules) {
if (CollectionUtils.isEmpty(rules)) {
return false;
}
return rules.stream()
.anyMatch(e -> Objects.equals(e.getRowPermission(), getRulePermissionId()));
}
}

View File

@ -0,0 +1,79 @@
package cn.axzo.framework.datapermission.dataObject;
import cn.axzo.framework.datapermission.context.DataPermissionContextHolder;
import cn.axzo.karma.client.feign.organization.OrganizationalNodeApi;
import cn.axzo.karma.client.feign.organization.request.PageOrganizationNodeReq;
import cn.axzo.karma.client.feign.organization.response.OrganizationalNodeResp;
import lombok.RequiredArgsConstructor;
import org.springframework.util.CollectionUtils;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import static cn.axzo.framework.datapermission.context.DataPermissionContextHolder.DEPARTMENT_SUBORDINATE;
@RequiredArgsConstructor
public class DepartmentAndSubordinateRule implements DataObjectRule {
private final OrganizationalNodeApi organizationalNodeApi;
/**
* 本部门及以下数据规则id
*/
private static final Integer RULE_PERMISSION_ID = 4;
@Override
public Integer getRulePermissionId() {
return RULE_PERMISSION_ID;
}
@Override
public void resolve(DataObjectRuleContext context) {
DataPermissionContextHolder.DataPermissionContext dataPermissionContext = DataPermissionContextHolder.get();
List<OrganizationalNodeResp> nodes = organizationalNodeApi.page(PageOrganizationNodeReq.builder()
.personId(dataPermissionContext.getPersonId())
.ouId(dataPermissionContext.getOuId())
.needSubordinate(true)
.build())
.getData()
.getData();
if (CollectionUtils.isEmpty(nodes)) {
return;
}
Set<Long> nodeIds = nodes.stream()
.map(OrganizationalNodeResp::getId)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
resolveNodes(nodeIds, nodes);
dataPermissionContext.putPermissionData(DEPARTMENT_SUBORDINATE, nodeIds);
context.addNodeIds(nodeIds);
}
private void resolveNodes(Set<Long> nodeIds, List<OrganizationalNodeResp> nodes) {
if (CollectionUtils.isEmpty(nodes)) {
return;
}
Set<Long> subordinateIds = nodes.stream()
.map(OrganizationalNodeResp::getSubordinateOuNodes)
.filter(Objects::nonNull)
.flatMap(Collection::stream)
.map(OrganizationalNodeResp::getId)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
if (CollectionUtils.isEmpty(subordinateIds)) {
return;
}
nodeIds.addAll(subordinateIds);
nodes.forEach(e -> resolveNodes(nodeIds, e.getSubordinateOuNodes()));
}
}

View File

@ -0,0 +1,58 @@
package cn.axzo.framework.datapermission.dataObject;
import cn.axzo.framework.datapermission.context.DataPermissionContextHolder;
import cn.axzo.karma.client.feign.organization.OrganizationalNodeApi;
import cn.axzo.karma.client.feign.organization.request.PageOrganizationNodeReq;
import cn.axzo.karma.client.feign.organization.response.OrganizationalNodeResp;
import lombok.RequiredArgsConstructor;
import org.springframework.util.CollectionUtils;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import static cn.axzo.framework.datapermission.context.DataPermissionContextHolder.DEPARTMENT_ONLY;
/**
* 本部门数据规则
*/
@RequiredArgsConstructor
public class DepartmentOnlyRule implements DataObjectRule {
private final OrganizationalNodeApi organizationalNodeApi;
/**
* 本部门数据规则id
*/
private static final Integer RULE_PERMISSION_ID = 3;
@Override
public Integer getRulePermissionId() {
return RULE_PERMISSION_ID;
}
@Override
public void resolve(DataObjectRuleContext context) {
DataPermissionContextHolder.DataPermissionContext dataPermissionContext = DataPermissionContextHolder.get();
List<OrganizationalNodeResp> nodes = organizationalNodeApi.page(PageOrganizationNodeReq.builder()
.personId(dataPermissionContext.getPersonId())
.ouId(dataPermissionContext.getOuId())
.build())
.getData()
.getData();
if (CollectionUtils.isEmpty(nodes)) {
return;
}
Set<Long> nodeIds = nodes.stream()
.map(OrganizationalNodeResp::getId)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
context.addNodeIds(nodeIds);
dataPermissionContext.putPermissionData(DEPARTMENT_ONLY, nodeIds);
}
}

View File

@ -0,0 +1,38 @@
package cn.axzo.framework.datapermission.dataObject;
import cn.axzo.karma.client.feign.tyr.response.MatchDataObjectResp;
import lombok.AllArgsConstructor;
import org.springframework.util.CollectionUtils;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
@AllArgsConstructor
public class MatchRuleFactory {
private final List<DataObjectRule> dataObjectRules;
public Optional<DataObjectRule.DataObjectRuleContext> matchAndResolve(MatchDataObjectResp matchDataObjectResp) {
if (Objects.isNull(matchDataObjectResp) || CollectionUtils.isEmpty(matchDataObjectResp.getRules())) {
return Optional.empty();
}
List<DataObjectRule> matchedRules = dataObjectRules.stream()
.filter(e -> e.match(matchDataObjectResp.getRules()))
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(matchedRules)) {
return Optional.empty();
}
DataObjectRule.DataObjectRuleContext dataObjectRuleContext = DataObjectRule.DataObjectRuleContext.builder().build();
for (DataObjectRule matchedRule : matchedRules) {
matchedRule.resolve(dataObjectRuleContext);
}
return Optional.ofNullable(dataObjectRuleContext);
}
}

View File

@ -0,0 +1,82 @@
package cn.axzo.framework.datapermission.dataObject;
import cn.axzo.framework.datapermission.context.DataPermissionContextHolder;
import cn.axzo.karma.client.feign.organization.OrganizationalNodeUserApi;
import cn.axzo.karma.client.feign.organization.request.PageOrganizationNodeUserReq;
import cn.axzo.karma.client.feign.organization.response.OrganizationalNodeUserResp;
import lombok.RequiredArgsConstructor;
import org.springframework.util.CollectionUtils;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import static cn.axzo.framework.datapermission.context.DataPermissionContextHolder.SELF_SUBORDINATE;
/**
* 本人及下属数据规则
*/
@RequiredArgsConstructor
public class SelfAndSubordinateRule implements DataObjectRule {
private final OrganizationalNodeUserApi organizationalNodeUserApi;
/**
* 本人及下属数据规则id
*/
private static final Integer RULE_PERMISSION_ID = 2;
@Override
public Integer getRulePermissionId() {
return RULE_PERMISSION_ID;
}
@Override
public void resolve(DataObjectRuleContext context) {
DataPermissionContextHolder.DataPermissionContext dataPermissionContext = DataPermissionContextHolder.get();
List<OrganizationalNodeUserResp> nodeUsers = organizationalNodeUserApi.page(PageOrganizationNodeUserReq.builder()
.personId(dataPermissionContext.getPersonId())
.ouId(dataPermissionContext.getOuId())
.needSubordinate(true)
.build())
.getData()
.getData();
if (CollectionUtils.isEmpty(nodeUsers)) {
return;
}
Set<Long> personIds = nodeUsers.stream()
.map(OrganizationalNodeUserResp::getPersonId)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
resolveNodeUsers(personIds, nodeUsers);
dataPermissionContext.putPermissionData(SELF_SUBORDINATE, personIds);
context.addPersonIds(personIds);
}
private void resolveNodeUsers(Set<Long> personIds, List<OrganizationalNodeUserResp> nodeUsers) {
if (CollectionUtils.isEmpty(nodeUsers)) {
return;
}
Set<Long> subordinatePersonIds = nodeUsers.stream()
.map(OrganizationalNodeUserResp::getSubordinateOuNodeUsers)
.filter(Objects::nonNull)
.flatMap(Collection::stream)
.map(OrganizationalNodeUserResp::getPersonId)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
if (CollectionUtils.isEmpty(subordinatePersonIds)) {
return;
}
personIds.addAll(subordinatePersonIds);
nodeUsers.forEach(e -> resolveNodeUsers(personIds, e.getSubordinateOuNodeUsers()));
}
}

View File

@ -0,0 +1,28 @@
package cn.axzo.framework.datapermission.dataObject;
import cn.axzo.framework.datapermission.context.DataPermissionContextHolder;
import com.google.common.collect.Sets;
import lombok.RequiredArgsConstructor;
/**
* 仅本人数据规则
*/
@RequiredArgsConstructor
public class SelfOnlyRule implements DataObjectRule {
/**
* 仅本人数据的规则id
*/
private static final Integer RULE_PERMISSION_ID = 1;
@Override
public Integer getRulePermissionId() {
return RULE_PERMISSION_ID;
}
@Override
public void resolve(DataObjectRuleContext context) {
DataPermissionContextHolder.DataPermissionContext dataPermissionContext = DataPermissionContextHolder.get();
context.addPersonIds(Sets.newHashSet(dataPermissionContext.getPersonId()));
}
}

View File

@ -0,0 +1,58 @@
package cn.axzo.framework.datapermission.dataObject;
import cn.axzo.framework.datapermission.context.DataPermissionContextHolder;
import cn.axzo.karma.client.feign.organization.OrganizationalNodeApi;
import cn.axzo.karma.client.feign.organization.request.QueryOuMultiConditionalReq;
import cn.axzo.karma.client.feign.organization.response.QueryOuMultiConditionalResp;
import lombok.RequiredArgsConstructor;
import org.springframework.util.CollectionUtils;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import static cn.axzo.framework.datapermission.context.DataPermissionContextHolder.UNIT_ALL_SUBORDINATE;
/**
* 本单位及以下协同直属+合作单位数据
*/
@RequiredArgsConstructor
public class UnitAllSubordinateRule implements DataObjectRule {
private final OrganizationalNodeApi organizationalNodeApi;
/**
* 本单位及以下协同直属+合作单位数据的规则id
*/
private static final Integer RULE_PERMISSION_ID = 8;
@Override
public Integer getRulePermissionId() {
return RULE_PERMISSION_ID;
}
@Override
public void resolve(DataObjectRuleContext context) {
DataPermissionContextHolder.DataPermissionContext dataPermissionContext = DataPermissionContextHolder.get();
QueryOuMultiConditionalResp node = organizationalNodeApi.queryOuMultiConditional(QueryOuMultiConditionalReq.builder()
.workspaceId(dataPermissionContext.getWorkspaceId())
.ouId(dataPermissionContext.getOuId())
.needDirectlyAndCooperateWithDescendant(true)
.build())
.getData();
if (Objects.isNull(node) || CollectionUtils.isEmpty(node.getBasicNodeUsers())) {
return;
}
Set<Long> nodeIds = node.getBasicNodeUsers().stream()
.map(QueryOuMultiConditionalResp.BasicNodeUser::getNodeId)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
context.addNodeIds(nodeIds);
dataPermissionContext.putPermissionData(UNIT_ALL_SUBORDINATE, nodeIds);
}
}

View File

@ -0,0 +1,58 @@
package cn.axzo.framework.datapermission.dataObject;
import cn.axzo.framework.datapermission.context.DataPermissionContextHolder;
import cn.axzo.karma.client.feign.organization.OrganizationalNodeApi;
import cn.axzo.karma.client.feign.organization.request.QueryOuMultiConditionalReq;
import cn.axzo.karma.client.feign.organization.response.QueryOuMultiConditionalResp;
import lombok.RequiredArgsConstructor;
import org.springframework.util.CollectionUtils;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import static cn.axzo.framework.datapermission.context.DataPermissionContextHolder.UNIT_COOPERATE_SUBORDINATE;
/**
* 本单位及下级协同直属+合作单位数据
*/
@RequiredArgsConstructor
public class UnitCooperateSubordinateRule implements DataObjectRule {
private final OrganizationalNodeApi organizationalNodeApi;
/**
* 本单位及下级协同直属+合作单位数据的规则id
*/
private static final Integer RULE_PERMISSION_ID = 7;
@Override
public Integer getRulePermissionId() {
return RULE_PERMISSION_ID;
}
@Override
public void resolve(DataObjectRuleContext context) {
DataPermissionContextHolder.DataPermissionContext dataPermissionContext = DataPermissionContextHolder.get();
QueryOuMultiConditionalResp node = organizationalNodeApi.queryOuMultiConditional(QueryOuMultiConditionalReq.builder()
.workspaceId(dataPermissionContext.getWorkspaceId())
.ouId(dataPermissionContext.getOuId())
.needDirectlyAndCooperate(true)
.build())
.getData();
if (Objects.isNull(node) || CollectionUtils.isEmpty(node.getBasicNodeUsers())) {
return;
}
Set<Long> nodeIds = node.getBasicNodeUsers().stream()
.map(QueryOuMultiConditionalResp.BasicNodeUser::getNodeId)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
context.addNodeIds(nodeIds);
dataPermissionContext.putPermissionData(UNIT_COOPERATE_SUBORDINATE, nodeIds);
}
}

View File

@ -0,0 +1,58 @@
package cn.axzo.framework.datapermission.dataObject;
import cn.axzo.framework.datapermission.context.DataPermissionContextHolder;
import cn.axzo.karma.client.feign.organization.OrganizationalNodeApi;
import cn.axzo.karma.client.feign.organization.request.QueryOuMultiConditionalReq;
import cn.axzo.karma.client.feign.organization.response.QueryOuMultiConditionalResp;
import lombok.RequiredArgsConstructor;
import org.springframework.util.CollectionUtils;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import static cn.axzo.framework.datapermission.context.DataPermissionContextHolder.UNIT_DIRECT_SUBORDINATE;
/**
* 本单位及下级直属单位数据
*/
@RequiredArgsConstructor
public class UnitDirectSubordinateRule implements DataObjectRule {
private final OrganizationalNodeApi organizationalNodeApi;
/**
* 本单位及下级直属单位数据的规则id
*/
private static final Integer RULE_PERMISSION_ID = 6;
@Override
public Integer getRulePermissionId() {
return RULE_PERMISSION_ID;
}
@Override
public void resolve(DataObjectRuleContext context) {
DataPermissionContextHolder.DataPermissionContext dataPermissionContext = DataPermissionContextHolder.get();
QueryOuMultiConditionalResp node = organizationalNodeApi.queryOuMultiConditional(QueryOuMultiConditionalReq.builder()
.workspaceId(dataPermissionContext.getWorkspaceId())
.ouId(dataPermissionContext.getOuId())
.needDirectlyUnder(true)
.build())
.getData();
if (Objects.isNull(node) || CollectionUtils.isEmpty(node.getBasicNodeUsers())) {
return;
}
Set<Long> nodeIds = node.getBasicNodeUsers().stream()
.map(QueryOuMultiConditionalResp.BasicNodeUser::getNodeId)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
context.addNodeIds(nodeIds);
dataPermissionContext.putPermissionData(UNIT_DIRECT_SUBORDINATE, nodeIds);
}
}

View File

@ -0,0 +1,57 @@
package cn.axzo.framework.datapermission.dataObject;
import cn.axzo.framework.datapermission.context.DataPermissionContextHolder;
import cn.axzo.karma.client.feign.organization.OrganizationalNodeApi;
import cn.axzo.karma.client.feign.organization.request.QueryOuMultiConditionalReq;
import cn.axzo.karma.client.feign.organization.response.QueryOuMultiConditionalResp;
import lombok.RequiredArgsConstructor;
import org.springframework.util.CollectionUtils;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import static cn.axzo.framework.datapermission.context.DataPermissionContextHolder.UNIT_ONLY_EXCLUDE_TEAM;
/**
* 仅本单位数据(不包含班组)
*/
@RequiredArgsConstructor
public class UnitOnlyExcludeTeamRule implements DataObjectRule {
private final OrganizationalNodeApi organizationalNodeApi;
/**
* 仅本单位数据(不包含班组)的规则id
*/
private static final Integer RULE_PERMISSION_ID = 10;
@Override
public Integer getRulePermissionId() {
return RULE_PERMISSION_ID;
}
@Override
public void resolve(DataObjectRuleContext context) {
DataPermissionContextHolder.DataPermissionContext dataPermissionContext = DataPermissionContextHolder.get();
QueryOuMultiConditionalResp node = organizationalNodeApi.queryOuMultiConditional(QueryOuMultiConditionalReq.builder()
.workspaceId(dataPermissionContext.getWorkspaceId())
.ouId(dataPermissionContext.getOuId())
.build())
.getData();
if (Objects.isNull(node) || CollectionUtils.isEmpty(node.getBasicNodeUsers())) {
return;
}
Set<Long> nodeIds = node.getBasicNodeUsers().stream()
.map(QueryOuMultiConditionalResp.BasicNodeUser::getNodeId)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
context.addNodeIds(nodeIds);
dataPermissionContext.putPermissionData(UNIT_ONLY_EXCLUDE_TEAM, nodeIds);
}
}

View File

@ -0,0 +1,58 @@
package cn.axzo.framework.datapermission.dataObject;
import cn.axzo.framework.datapermission.context.DataPermissionContextHolder;
import cn.axzo.karma.client.feign.organization.OrganizationalNodeApi;
import cn.axzo.karma.client.feign.organization.request.QueryOuMultiConditionalReq;
import cn.axzo.karma.client.feign.organization.response.QueryOuMultiConditionalResp;
import lombok.RequiredArgsConstructor;
import org.springframework.util.CollectionUtils;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import static cn.axzo.framework.datapermission.context.DataPermissionContextHolder.UNIT_ONLY;
/**
* 仅本单位数据(包含班组)
*/
@RequiredArgsConstructor
public class UnitOnlyRule implements DataObjectRule {
private final OrganizationalNodeApi organizationalNodeApi;
/**
* 仅本单位数据(包含班组)的规则id
*/
private static final Integer RULE_PERMISSION_ID = 5;
@Override
public Integer getRulePermissionId() {
return RULE_PERMISSION_ID;
}
@Override
public void resolve(DataObjectRuleContext context) {
DataPermissionContextHolder.DataPermissionContext dataPermissionContext = DataPermissionContextHolder.get();
QueryOuMultiConditionalResp node = organizationalNodeApi.queryOuMultiConditional(QueryOuMultiConditionalReq.builder()
.workspaceId(dataPermissionContext.getWorkspaceId())
.ouId(dataPermissionContext.getOuId())
.needTeam(true)
.build())
.getData();
if (Objects.isNull(node) || CollectionUtils.isEmpty(node.getBasicNodeUsers())) {
return;
}
Set<Long> nodeIds = node.getBasicNodeUsers().stream()
.map(QueryOuMultiConditionalResp.BasicNodeUser::getNodeId)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
context.addNodeIds(nodeIds);
dataPermissionContext.putPermissionData(UNIT_ONLY, nodeIds);
}
}

View File

@ -0,0 +1,58 @@
package cn.axzo.framework.datapermission.dataObject;
import cn.axzo.framework.datapermission.context.DataPermissionContextHolder;
import cn.axzo.karma.client.feign.organization.OrganizationalNodeApi;
import cn.axzo.karma.client.feign.organization.request.PageOrganizationNodeReq;
import cn.axzo.karma.client.feign.organization.response.OrganizationalNodeResp;
import lombok.RequiredArgsConstructor;
import org.springframework.util.CollectionUtils;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import static cn.axzo.framework.datapermission.context.DataPermissionContextHolder.WORKSPACE;
/**
* 本项目部数据
*/
@RequiredArgsConstructor
public class WorkspaceRule implements DataObjectRule {
private final OrganizationalNodeApi organizationalNodeApi;
/**
* 本项目部数据的规则id
*/
private static final Integer RULE_PERMISSION_ID = 9;
@Override
public Integer getRulePermissionId() {
return RULE_PERMISSION_ID;
}
@Override
public void resolve(DataObjectRuleContext context) {
DataPermissionContextHolder.DataPermissionContext dataPermissionContext = DataPermissionContextHolder.get();
List<OrganizationalNodeResp> nodes = organizationalNodeApi.page(PageOrganizationNodeReq.builder()
.workspaceId(dataPermissionContext.getWorkspaceId())
.build())
.getData()
.getData();
if (CollectionUtils.isEmpty(nodes)) {
return;
}
Set<Long> nodeIds = nodes.stream()
.map(OrganizationalNodeResp::getId)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
context.addNodeIds(nodeIds);
dataPermissionContext.putPermissionData(WORKSPACE, nodeIds);
}
}

View File

@ -1,10 +1,8 @@
package cn.axzo.framework.datapermission.rule; package cn.axzo.framework.datapermission.rule;
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
import net.sf.jsqlparser.expression.Alias; import net.sf.jsqlparser.expression.Alias;
import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.Expression;
import java.util.List;
import java.util.Set; import java.util.Set;
/** /**
@ -16,5 +14,4 @@ public interface DataPermissionRule {
Set<String> getTableName(); Set<String> getTableName();
Expression getExpression(String tableName, Alias tableAlias); Expression getExpression(String tableName, Alias tableAlias);
} }

View File

@ -1,13 +1,19 @@
package cn.axzo.framework.datapermission.rule; package cn.axzo.framework.datapermission.rule;
import cn.axzo.karma.client.feign.FlowSupportApi; import cn.axzo.framework.datapermission.context.DataPermissionContextHolder;
import cn.hutool.core.collection.ListUtil; import cn.axzo.framework.datapermission.dataObject.DataObjectRule;
import cn.axzo.framework.datapermission.dataObject.MatchRuleFactory;
import cn.axzo.karma.client.feign.tyr.DataObjectApi;
import cn.axzo.karma.client.feign.tyr.request.MatchDataObjectReq;
import cn.axzo.karma.client.feign.tyr.response.MatchDataObjectResp;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.TableInfo; import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper; import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.StringPool; import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.extension.activerecord.Model; import com.baomidou.mybatisplus.extension.activerecord.Model;
import lombok.AllArgsConstructor; import lombok.Builder;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.Alias; import net.sf.jsqlparser.expression.Alias;
import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue; import net.sf.jsqlparser.expression.LongValue;
@ -17,25 +23,25 @@ import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList; import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.InExpression; import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.schema.Column; import net.sf.jsqlparser.schema.Column;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
* 基于personId租户单位部门的数据权限
* @author tanjie@axzo.cn * @author tanjie@axzo.cn
* @date 2024/5/30 18:35 * @date 2024/5/30 18:35
*/ */
@Builder
@RequiredArgsConstructor @RequiredArgsConstructor
public class DataPermissionDefaultRule implements DataPermissionRule { @Slf4j
public class OrgDefaultRule implements DataPermissionRule {
static final Expression EXPRESSION_NULL = new NullValue(); static final Expression EXPRESSION_NULL = new NullValue();
private static final String DEFAULT_PERSON = "person_id"; private static final String DEFAULT_PERSON = "person_id";
@ -54,9 +60,9 @@ public class DataPermissionDefaultRule implements DataPermissionRule {
protected final Set<String> tableName =new HashSet<>(); protected final Set<String> tableName =new HashSet<>();
private final DataObjectApi dataObjectApi;
//// TODO: 2024/6/3 查询数据权限的api private final MatchRuleFactory matchRuleFactory;
private final FlowSupportApi permissionApi;
@Override @Override
public Set<String> getTableName() { public Set<String> getTableName() {
@ -66,22 +72,41 @@ public class DataPermissionDefaultRule implements DataPermissionRule {
@Override @Override
public Expression getExpression(String tableName, Alias tableAlias) { public Expression getExpression(String tableName, Alias tableAlias) {
// TODO: 2024/5/31 可访问的数据 @李龙 Optional<MatchDataObjectResp> matchDataObjectOptional = this.matchRule();
List<Long> nodeIds = ListUtil.of(1L, 2L, 3L); if (!matchDataObjectOptional.isPresent() || CollectionUtils.isEmpty(matchDataObjectOptional.get().getRules())) {
List<Long> personIds = ListUtil.of(110L, 223L); log.warn("no match data rule, dataPermissionContext{}", JSONObject.toJSONString(DataPermissionContextHolder.get()));
return EXPRESSION_NULL;
}
MatchDataObjectResp matchDataObject = matchDataObjectOptional.get();
Optional<DataObjectRule.DataObjectRuleContext> dataObjectRuleContextOpt = matchRuleFactory.matchAndResolve(matchDataObject);
if (!dataObjectRuleContextOpt.isPresent()) {
return EXPRESSION_NULL;
}
DataObjectRule.DataObjectRuleContext dataObjectRuleContext = dataObjectRuleContextOpt.get();
Set<Long> nodeIds = dataObjectRuleContext.getNodeIds();
Set<Long> personIds = dataObjectRuleContext.getPersonIds();
Expression deptExpression = buildExpression(tableName, tableAlias, nodeIds); Expression deptExpression = buildExpression(tableName, tableAlias, nodeIds);
Expression userExpression = buildExpression(tableName, tableAlias, personIds); Expression userExpression = buildExpression(tableName, tableAlias, personIds);
if (deptExpression == null && userExpression == null) { if (deptExpression == null && userExpression == null) {
// 未获取到数据权限 返回NULL 不查询数据 // 未获取到数据权限 返回NULL 不查询数据
return EXPRESSION_NULL; return EXPRESSION_NULL;
} }
if (deptExpression == null) { if (deptExpression == null) {
return userExpression; return userExpression;
} }
if (userExpression == null) { if (userExpression == null) {
return deptExpression; return deptExpression;
} }
// 如果两个都有用or WHERE (organizational_Node_id IN ? OR person_id in ?) // 如果两个都有用or WHERE (organizational_Node_id IN ? OR person_id in ?)
return new Parenthesis(new OrExpression(deptExpression, userExpression)); return new Parenthesis(new OrExpression(deptExpression, userExpression));
@ -96,7 +121,7 @@ public class DataPermissionDefaultRule implements DataPermissionRule {
* @param ids 可能是nodeids 也可以是personIds * @param ids 可能是nodeids 也可以是personIds
* @return * @return
*/ */
private Expression buildExpression(String tableName, Alias tableAlias, List<Long> ids) { private Expression buildExpression(String tableName, Alias tableAlias, Set<Long> ids) {
// 如果不存在配置则无需作为条件 // 如果不存在配置则无需作为条件
String columnName = nodeTable.get(tableName); String columnName = nodeTable.get(tableName);
if (!StringUtils.hasText(columnName)) { if (!StringUtils.hasText(columnName)) {
@ -133,15 +158,6 @@ public class DataPermissionDefaultRule implements DataPermissionRule {
tableName.add(tableInfo.getTableName()); tableName.add(tableInfo.getTableName());
} }
/**
* 添加组织节点所对应的字段 字段默认使用organizational_node_id
*
* @param entityClass 表所对应的entity
*/
public void addNodeColumn(Class<? extends Model<?>> entityClass) {
addNodeColumn(entityClass, DEFAULT_NODE);
}
/** /**
* 添加personId所对应的字段 * 添加personId所对应的字段
* *
@ -157,12 +173,34 @@ public class DataPermissionDefaultRule implements DataPermissionRule {
tableName.add(tableInfo.getTableName()); tableName.add(tableInfo.getTableName());
} }
/** private Optional<MatchDataObjectResp> matchRule() {
* 字段默认使用person_id
* if (invalidContext()) {
* @param entityClass 表所对应的entity return Optional.empty();
*/ }
public void addPersonColumn(Class<? extends Model<?>> entityClass) {
addPersonColumn(entityClass, DEFAULT_PERSON); DataPermissionContextHolder.DataPermissionContext dataPermissionContext = DataPermissionContextHolder.get();
MatchDataObjectReq matchDataObjectReq = MatchDataObjectReq.builder()
.dataObjectCode(dataPermissionContext.getDataPermission().bizCode())
.ouId(dataPermissionContext.getOuId())
.workspaceId(dataPermissionContext.getWorkspaceId())
.personId(dataPermissionContext.getPersonId())
.build();
return Optional.ofNullable(dataObjectApi.match(matchDataObjectReq).getData());
}
private boolean invalidContext() {
DataPermissionContextHolder.DataPermissionContext dataPermissionContext = DataPermissionContextHolder.get();
return dataPermissionContext == null
|| dataPermissionContext.getDataPermission() == null
|| StringsUtil.isBlank(dataPermissionContext.getDataPermission().bizCode())
|| !dataPermissionContext.getDataPermission().enable()
|| dataPermissionContext.getOuId() == null
|| dataPermissionContext.getWorkspaceId() == null
|| dataPermissionContext.getPersonId() == null;
} }
} }

View File

@ -0,0 +1,27 @@
package cn.axzo.framework.datapermission.rule;
public class StringsUtil {
public static boolean isNotBlank(CharSequence cs) {
return !isBlank(cs);
}
public static boolean isBlank(CharSequence cs) {
int strLen = length(cs);
if (strLen == 0) {
return true;
} else {
for(int i = 0; i < strLen; ++i) {
if (!Character.isWhitespace(cs.charAt(i))) {
return false;
}
}
return true;
}
}
public static int length(CharSequence cs) {
return cs == null ? 0 : cs.length();
}
}