|
@@ -3,25 +3,28 @@ package org.hswebframework.web.organizational.authorization.simple.handler;
|
|
|
import lombok.EqualsAndHashCode;
|
|
|
import lombok.Getter;
|
|
|
import lombok.Setter;
|
|
|
+import lombok.SneakyThrows;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import org.hswebframework.ezorm.core.dsl.Query;
|
|
|
import org.hswebframework.web.authorization.Authentication;
|
|
|
import org.hswebframework.web.authorization.Permission;
|
|
|
+import org.hswebframework.web.authorization.User;
|
|
|
import org.hswebframework.web.authorization.access.DataAccessConfig;
|
|
|
import org.hswebframework.web.authorization.access.DataAccessHandler;
|
|
|
+import org.hswebframework.web.authorization.access.UserAttachEntity;
|
|
|
import org.hswebframework.web.authorization.define.AuthorizingContext;
|
|
|
import org.hswebframework.web.authorization.define.Phased;
|
|
|
import org.hswebframework.web.authorization.exception.AccessDenyException;
|
|
|
import org.hswebframework.web.authorization.exception.UnAuthorizedException;
|
|
|
import org.hswebframework.web.bean.FastBeanCopier;
|
|
|
import org.hswebframework.web.commons.entity.Entity;
|
|
|
+import org.hswebframework.web.commons.entity.PagerResult;
|
|
|
import org.hswebframework.web.commons.entity.RecordCreationEntity;
|
|
|
import org.hswebframework.web.commons.entity.factory.EntityFactory;
|
|
|
import org.hswebframework.web.commons.entity.param.QueryParamEntity;
|
|
|
import org.hswebframework.web.controller.QueryController;
|
|
|
import org.hswebframework.web.controller.message.ResponseMessage;
|
|
|
import org.hswebframework.web.organizational.authorization.PersonnelAuthentication;
|
|
|
-import org.hswebframework.web.organizational.authorization.PersonnelAuthenticationHolder;
|
|
|
import org.hswebframework.web.organizational.authorization.access.*;
|
|
|
import org.hswebframework.web.organizational.authorization.simple.ScopeByUserDataAccessConfig;
|
|
|
import org.hswebframework.web.service.QueryService;
|
|
@@ -30,6 +33,7 @@ import org.springframework.http.ResponseEntity;
|
|
|
import org.springframework.util.ClassUtils;
|
|
|
import org.springframework.util.ReflectionUtils;
|
|
|
|
|
|
+import java.lang.reflect.Field;
|
|
|
import java.lang.reflect.Method;
|
|
|
import java.util.*;
|
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
@@ -66,14 +70,19 @@ public class ScopeByUserHandler implements DataAccessHandler {
|
|
|
}
|
|
|
|
|
|
protected boolean doUpdateAccess(ScopeByUserDataAccessConfig config, AuthorizingContext context) {
|
|
|
- //获取注解
|
|
|
+ //获取id参数
|
|
|
Object id = context.getParamContext()
|
|
|
.<String>getParameter(context.getDefinition().getDataAccessDefinition().getIdParameterName())
|
|
|
.orElse(null);
|
|
|
if (id == null) {
|
|
|
return true;
|
|
|
}
|
|
|
- PersonnelAuthentication authentication = PersonnelAuthentication.current().orElseThrow(UnAuthorizedException::new);
|
|
|
+ PersonnelAuthentication authentication = PersonnelAuthentication
|
|
|
+ .current().orElse(null);
|
|
|
+ if (authentication == null) {
|
|
|
+ log.warn("当前用户没有关联人员信息!");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
ScopeInfo scopeInfo = getScope(config, authentication);
|
|
|
if (scopeInfo.isEmpty()) {
|
|
|
return false;
|
|
@@ -101,10 +110,10 @@ public class ScopeByUserHandler implements DataAccessHandler {
|
|
|
Object entity = queryService.selectByPk(id);
|
|
|
if (null != entity) {
|
|
|
String targetId = controllerCache.targetIdGetter.apply(entity);
|
|
|
- log.debug("执行数据权限控制,scope:{},target:{}", scopeInfo.scope, targetId);
|
|
|
if (targetId == null) {
|
|
|
return true;
|
|
|
}
|
|
|
+ log.debug("执行数据权限控制,范围:{},结果:{}", scopeInfo.scope, targetId);
|
|
|
return scopeInfo.allScope.contains(controllerCache.targetIdGetter.apply(entity));
|
|
|
}
|
|
|
} else {
|
|
@@ -114,8 +123,9 @@ public class ScopeByUserHandler implements DataAccessHandler {
|
|
|
|
|
|
}
|
|
|
|
|
|
+ @SneakyThrows
|
|
|
private ScopeInfo getScope(ScopeByUserDataAccessConfig config, PersonnelAuthentication authentication) {
|
|
|
- String termType = null;
|
|
|
+ String termType = null, personTermType = "in";
|
|
|
Set<String> scope = null, allScope = null;
|
|
|
ScopeInfo scopeInfo = new ScopeInfo();
|
|
|
if (authentication == null) {
|
|
@@ -124,43 +134,65 @@ public class ScopeByUserHandler implements DataAccessHandler {
|
|
|
Consumer<Query<?, QueryParamEntity>> consumer;
|
|
|
|
|
|
switch (config.getScopeType()) {
|
|
|
+ case "OWN_PERSON":
|
|
|
+ termType = "in";
|
|
|
+ scope = Collections.singleton(authentication.getPersonnel().getId());
|
|
|
+ allScope = scope;
|
|
|
+ break;
|
|
|
+ case "OWN_USER":
|
|
|
+ termType = "in";
|
|
|
+ scope = Collections.singleton(Authentication
|
|
|
+ .current()
|
|
|
+ .map(Authentication::getUser)
|
|
|
+ .map(User::getId)
|
|
|
+ .orElseThrow(AccessDenyException::new));
|
|
|
+ allScope = scope;
|
|
|
+ break;
|
|
|
case DataAccessType.ORG_SCOPE:
|
|
|
termType = "user-in-org";
|
|
|
+ personTermType = "person-in-org";
|
|
|
scope = authentication.getRootOrgId();
|
|
|
allScope = config.isChildren() ? authentication.getAllOrgId() : scope;
|
|
|
break;
|
|
|
case DataAccessType.DEPARTMENT_SCOPE:
|
|
|
termType = "user-in-department";
|
|
|
+ personTermType = "person-in-department";
|
|
|
scope = authentication.getRootDepartmentId();
|
|
|
allScope = config.isChildren() ? authentication.getAllDepartmentId() : scope;
|
|
|
break;
|
|
|
case DataAccessType.POSITION_SCOPE:
|
|
|
termType = "user-in-position";
|
|
|
+ personTermType = "person-in-position";
|
|
|
scope = authentication.getRootPositionId();
|
|
|
allScope = config.isChildren() ? authentication.getAllPositionId() : scope;
|
|
|
break;
|
|
|
case DataAccessType.DISTRICT_SCOPE:
|
|
|
termType = "user-in-dist";
|
|
|
+ personTermType = "person-in-dist";
|
|
|
scope = authentication.getRootDistrictId();
|
|
|
allScope = config.isChildren() ? authentication.getAllDistrictId() : scope;
|
|
|
break;
|
|
|
case "CUSTOM_SCOPE_ORG":
|
|
|
termType = "user-in-org";
|
|
|
+ personTermType = "person-in-org";
|
|
|
scope = config.getScope();
|
|
|
allScope = scope;
|
|
|
break;
|
|
|
case "CUSTOM_SCOPE_POSITION":
|
|
|
termType = "user-in-position";
|
|
|
+ personTermType = "person-in-position";
|
|
|
scope = config.getScope();
|
|
|
allScope = scope;
|
|
|
break;
|
|
|
case "CUSTOM_SCOPE_DEPT":
|
|
|
termType = "user-in-department";
|
|
|
+ personTermType = "person-in-department";
|
|
|
scope = config.getScope();
|
|
|
allScope = scope;
|
|
|
break;
|
|
|
case "CUSTOM_SCOPE_DIST":
|
|
|
termType = "user-in-dist";
|
|
|
+ personTermType = "person-in-dist";
|
|
|
scope = config.getScope();
|
|
|
allScope = scope;
|
|
|
break;
|
|
@@ -174,7 +206,12 @@ public class ScopeByUserHandler implements DataAccessHandler {
|
|
|
scopeInfo.allScope = new ArrayList<>(allScope);
|
|
|
scopeInfo.termType = termType;
|
|
|
if (config.isChildren()) {
|
|
|
- scopeInfo.termType = termType + termType.concat("-child");
|
|
|
+ if (!termType.equals("in")) {
|
|
|
+ scopeInfo.termType = termType.concat("-child");
|
|
|
+ }
|
|
|
+ if (!personTermType.equals("in")) {
|
|
|
+ scopeInfo.personTermType = personTermType.concat("-child");
|
|
|
+ }
|
|
|
}
|
|
|
return scopeInfo;
|
|
|
|
|
@@ -182,6 +219,7 @@ public class ScopeByUserHandler implements DataAccessHandler {
|
|
|
|
|
|
class ScopeInfo {
|
|
|
String termType;
|
|
|
+ String personTermType;
|
|
|
|
|
|
List<String> scope;
|
|
|
List<String> allScope;
|
|
@@ -194,11 +232,33 @@ public class ScopeByUserHandler implements DataAccessHandler {
|
|
|
}
|
|
|
|
|
|
static Function<Object, String> defaultTargetIdGetter = entity -> {
|
|
|
- Map<String, String> userInfo = FastBeanCopier.copy(entity, new HashMap<>(),
|
|
|
- FastBeanCopier.include("creatorId"));
|
|
|
- return userInfo.get("creatorId");
|
|
|
+ Map<String, String> userInfo = FastBeanCopier.copy(entity, new HashMap<>(), FastBeanCopier.include("creatorId", "userId"));
|
|
|
+ return userInfo.getOrDefault("userId", userInfo.get("creatorId"));
|
|
|
};
|
|
|
|
|
|
+ protected Function<Object, String> createTargetIdGetter(Class entityClass, String... properties) {
|
|
|
+ String useProperty = null;
|
|
|
+ for (String property : properties) {
|
|
|
+ Field field = ReflectionUtils.findField(entityClass, property);
|
|
|
+ if (field != null) {
|
|
|
+ useProperty = property;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (useProperty == null) {
|
|
|
+ log.warn("类[{}]中未包含字段[{}],可能无法进行数据权限控制.", entityClass, Arrays.asList(properties));
|
|
|
+ }
|
|
|
+ return entity -> {
|
|
|
+ Map<String, String> userInfo = FastBeanCopier.copy(entity, new HashMap<>(), FastBeanCopier.include(properties));
|
|
|
+ for (String property : properties) {
|
|
|
+ String value = userInfo.get(property);
|
|
|
+ if (value != null) {
|
|
|
+ return value;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
static BiConsumer<Query<?, QueryParamEntity>, ScopeInfo> defaultQueryConsumer = (query, scopeInfo) -> {
|
|
|
query.and("creatorId", scopeInfo.termType, scopeInfo.scope);
|
|
|
};
|
|
@@ -237,59 +297,91 @@ public class ScopeByUserHandler implements DataAccessHandler {
|
|
|
class CacheKey {
|
|
|
private String className;
|
|
|
|
|
|
+ private String method;
|
|
|
+
|
|
|
private boolean children;
|
|
|
|
|
|
private String type;
|
|
|
+
|
|
|
+ private boolean queryController;
|
|
|
}
|
|
|
|
|
|
static Map<CacheKey, ControllerCache> cacheMap = new ConcurrentHashMap<>();
|
|
|
|
|
|
|
|
|
private ControllerCache getControllerCache(ScopeByUserDataAccessConfig config, AuthorizingContext context) {
|
|
|
+ Class controller = ClassUtils.getUserClass(context.getParamContext().getTarget().getClass());
|
|
|
CacheKey cacheKey = new CacheKey();
|
|
|
cacheKey.children = config.isChildren();
|
|
|
- cacheKey.className = ClassUtils.getUserClass(context.getParamContext().getTarget().getClass()).getName();
|
|
|
+ cacheKey.className = controller.getName();
|
|
|
+ cacheKey.method = context.getParamContext().getMethod().toString();
|
|
|
cacheKey.type = config.getScopeType();
|
|
|
+ cacheKey.queryController = context.getParamContext().getTarget() instanceof QueryController;
|
|
|
+ Class dataAccessEntityType = context.getDefinition().getDataAccessDefinition().getEntityType();
|
|
|
+
|
|
|
return cacheMap.computeIfAbsent(cacheKey, key -> {
|
|
|
ControllerCache controllerCache = new ControllerCache();
|
|
|
- if (context.getParamContext().getTarget() instanceof QueryController) {
|
|
|
- boolean children = config.isChildren();
|
|
|
- Class controller = ClassUtils.getUserClass(context.getParamContext().getTarget().getClass());
|
|
|
- Class entityClass = org.hswebframework.utils.ClassUtils.getGenericType(controller, 0);
|
|
|
- if (OrgAttachEntity.class.isAssignableFrom(entityClass) && config.getScopeType().contains("ORG")) {
|
|
|
- controllerCache.targetIdGetter = createGetter(OrgAttachEntity.class, OrgAttachEntity::getOrgId);
|
|
|
- controllerCache.queryConsumer = (query, scopeInfo) -> {
|
|
|
- query.and(getControlProperty(entityClass, OrgAttachEntity::getOrgIdProperty), children ? "org-child-in" : "in", scopeInfo.scope);
|
|
|
- };
|
|
|
- } else if (DepartmentAttachEntity.class.isAssignableFrom(entityClass) && config.getScopeType().contains("DEPT")) {
|
|
|
- controllerCache.targetIdGetter = createGetter(DepartmentAttachEntity.class, DepartmentAttachEntity::getDepartmentId);
|
|
|
- controllerCache.queryConsumer = (query, scopeInfo) -> {
|
|
|
- query.and(getControlProperty(entityClass, DepartmentAttachEntity::getDepartmentIdProperty), children ? "dept-child-in" : "in", scopeInfo.scope);
|
|
|
- };
|
|
|
- } else if (PositionAttachEntity.class.isAssignableFrom(entityClass) && config.getScopeType().contains("POS")) {
|
|
|
- controllerCache.targetIdGetter = createGetter(PositionAttachEntity.class, PositionAttachEntity::getPositionId);
|
|
|
- controllerCache.queryConsumer = (query, scopeInfo) -> {
|
|
|
- query.and(getControlProperty(entityClass, PositionAttachEntity::getPositionIdProperty), children ? "pos-child-in" : "in", scopeInfo.scope);
|
|
|
- };
|
|
|
- } else if (DistrictAttachEntity.class.isAssignableFrom(entityClass) && config.getScopeType().contains("DIST")) {
|
|
|
- controllerCache.targetIdGetter = createGetter(DistrictAttachEntity.class, DistrictAttachEntity::getDistrictId);
|
|
|
+ Class entityClass = dataAccessEntityType;
|
|
|
+ if (entityClass == Void.class) {
|
|
|
+ if (cacheKey.queryController) {
|
|
|
+ entityClass = org.hswebframework.utils.ClassUtils.getGenericType(controller, 0);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ boolean children = cacheKey.isChildren();
|
|
|
+ //控制机构
|
|
|
+ if (cacheKey.getType().contains("ORG") && OrgAttachEntity.class.isAssignableFrom(entityClass)) {
|
|
|
+ String property = getControlProperty(entityClass, OrgAttachEntity::getOrgIdProperty);
|
|
|
+ controllerCache.targetIdGetter = createGetter(OrgAttachEntity.class, OrgAttachEntity::getOrgId);
|
|
|
+ controllerCache.queryConsumer = (query, scopeInfo) -> {
|
|
|
+ query.and(property, children ? "org-child-in" : "in", scopeInfo.scope);
|
|
|
+ };
|
|
|
+ //部门
|
|
|
+ } else if (cacheKey.getType().contains("DEPT") && DepartmentAttachEntity.class.isAssignableFrom(entityClass)) {
|
|
|
+ String property = getControlProperty(entityClass, DepartmentAttachEntity::getDepartmentIdProperty);
|
|
|
+ controllerCache.targetIdGetter = createGetter(DepartmentAttachEntity.class, DepartmentAttachEntity::getDepartmentId);
|
|
|
+ controllerCache.queryConsumer = (query, scopeInfo) -> {
|
|
|
+ query.and(property, children ? "org-child-in" : "in", scopeInfo.scope);
|
|
|
+ };
|
|
|
+ //岗位
|
|
|
+ } else if (cacheKey.getType().contains("POS") && PositionAttachEntity.class.isAssignableFrom(entityClass)) {
|
|
|
+ String property = getControlProperty(entityClass, PositionAttachEntity::getPositionIdProperty);
|
|
|
+ controllerCache.targetIdGetter = createGetter(PositionAttachEntity.class, PositionAttachEntity::getPositionId);
|
|
|
+ controllerCache.queryConsumer = (query, scopeInfo) -> {
|
|
|
+ query.and(property, children ? "pos-child-in" : "in", scopeInfo.scope);
|
|
|
+ };
|
|
|
+ //行政区划
|
|
|
+ } else if (cacheKey.getType().contains("DIST") && DistrictAttachEntity.class.isAssignableFrom(entityClass)) {
|
|
|
+ String property = getControlProperty(entityClass, DistrictAttachEntity::getDistrictIdProperty);
|
|
|
+ controllerCache.targetIdGetter = createGetter(DistrictAttachEntity.class, DistrictAttachEntity::getDistrictId);
|
|
|
+ controllerCache.queryConsumer = (query, scopeInfo) -> {
|
|
|
+ query.and(property, children ? "dist-child-in" : "in", scopeInfo.scope);
|
|
|
+ };
|
|
|
+ //人员
|
|
|
+ } else if (cacheKey.getType().contains("PERSON") && PersonAttachEntity.class.isAssignableFrom(entityClass)) {
|
|
|
+ String property = getControlProperty(entityClass, PersonAttachEntity::getPersonIdProperty);
|
|
|
+ controllerCache.targetIdGetter = createGetter(PersonAttachEntity.class, PersonAttachEntity::getPersonId);
|
|
|
+ controllerCache.queryConsumer = (query, scopeInfo) -> {
|
|
|
+ query.and(property, scopeInfo.termType, scopeInfo.scope);
|
|
|
+ };
|
|
|
+ //根据用户控制
|
|
|
+ } else {
|
|
|
+ if (UserAttachEntity.class.isAssignableFrom(entityClass)) {
|
|
|
+ String property = getControlProperty(entityClass, UserAttachEntity::getUserIdProperty);
|
|
|
+ controllerCache.targetIdGetter = createGetter(UserAttachEntity.class, UserAttachEntity::getUserId);
|
|
|
controllerCache.queryConsumer = (query, scopeInfo) -> {
|
|
|
- query.and(getControlProperty(entityClass, DistrictAttachEntity::getDistrictIdProperty), children ? "dist-child-in" : "in", scopeInfo.scope);
|
|
|
+ query.and(property, scopeInfo.termType, scopeInfo.scope);
|
|
|
};
|
|
|
} else if (RecordCreationEntity.class.isAssignableFrom(entityClass)) {
|
|
|
+ String property = getControlProperty(entityClass, RecordCreationEntity::getCreatorIdProperty);
|
|
|
controllerCache.targetIdGetter = createGetter(RecordCreationEntity.class, RecordCreationEntity::getCreatorId);
|
|
|
controllerCache.queryConsumer = (query, scopeInfo) -> {
|
|
|
- query.and(getControlProperty(entityClass, RecordCreationEntity::getCreatorIdProperty), scopeInfo.termType, scopeInfo.scope);
|
|
|
+ query.and(property, scopeInfo.termType, scopeInfo.scope);
|
|
|
};
|
|
|
} else {
|
|
|
- String userIdField = getUserField(entityClass);
|
|
|
- controllerCache.targetIdGetter = entity -> {
|
|
|
- Map<String, String> userInfo = FastBeanCopier.copy(entity, new HashMap<>(),
|
|
|
- FastBeanCopier.include(userIdField));
|
|
|
- return userInfo.get(userIdField);
|
|
|
- };
|
|
|
+ String property = getUserField(entityClass);
|
|
|
+ controllerCache.targetIdGetter = createTargetIdGetter(entityClass, property);
|
|
|
controllerCache.queryConsumer = (query, scopeInfo) -> {
|
|
|
- query.and(userIdField, scopeInfo.termType, scopeInfo.scope);
|
|
|
+ query.and(property, scopeInfo.termType, scopeInfo.scope);
|
|
|
};
|
|
|
}
|
|
|
}
|
|
@@ -313,18 +405,24 @@ public class ScopeByUserHandler implements DataAccessHandler {
|
|
|
if (result == null) {
|
|
|
return true;
|
|
|
}
|
|
|
- if (result instanceof ResponseEntity) {
|
|
|
- result = ((ResponseEntity) result).getBody();
|
|
|
- }
|
|
|
- if (result instanceof ResponseMessage) {
|
|
|
- result = ((ResponseMessage) result).getResult();
|
|
|
- }
|
|
|
- String value = controllerCache.targetIdGetter.apply(result);
|
|
|
- log.debug("执行数据权限控制[{}],scope:{},target:{}", config.getScopeTypeName(), scopeInfo.scope, value);
|
|
|
- if (value == null) {
|
|
|
- return true;
|
|
|
+ result = getRealResult(result);
|
|
|
+ Predicate<Object> predicate = (o) -> {
|
|
|
+ String value = controllerCache.targetIdGetter.apply(o);
|
|
|
+ if (value == null) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ log.debug("执行数据权限控制[{}],scope:{},target:{}", config.getScopeTypeName(), scopeInfo.scope, value);
|
|
|
+ return scopeInfo.allScope.contains(value);
|
|
|
+ };
|
|
|
+ if (result instanceof Collection) {
|
|
|
+ Collection<?> res = ((Collection) result);
|
|
|
+ if (res.isEmpty()) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ return res.stream().allMatch(predicate);
|
|
|
+ } else {
|
|
|
+ return predicate.test(result);
|
|
|
}
|
|
|
- return scopeInfo.allScope.contains(value);
|
|
|
}
|
|
|
|
|
|
Entity entity = context.getParamContext()
|
|
@@ -339,9 +437,11 @@ public class ScopeByUserHandler implements DataAccessHandler {
|
|
|
if (entity instanceof QueryParamEntity) {
|
|
|
QueryParamEntity param = ((QueryParamEntity) entity);
|
|
|
param.toNestQuery(query -> {
|
|
|
- log.debug("执行查询数据权限控制[{}],scope:{}", config.getScopeTypeName(), scopeInfo.scope);
|
|
|
+ log.debug("执行查询数据权限控制[{}],范围:{}", config.getScopeTypeName(), scopeInfo.scope);
|
|
|
controllerCache.queryConsumer.accept(query, scopeInfo);
|
|
|
});
|
|
|
+ } else {
|
|
|
+ log.warn("方法[{}]未使用动态查询参数[QueryParamEntity],无法进行数据权限控制!", context.getParamContext().getMethod());
|
|
|
}
|
|
|
return true;
|
|
|
}
|
|
@@ -354,4 +454,17 @@ public class ScopeByUserHandler implements DataAccessHandler {
|
|
|
|
|
|
return "creatorId";
|
|
|
}
|
|
|
+
|
|
|
+ protected Object getRealResult(Object result) {
|
|
|
+ if (result instanceof ResponseEntity) {
|
|
|
+ result = ((ResponseEntity) result).getBody();
|
|
|
+ }
|
|
|
+ if (result instanceof ResponseMessage) {
|
|
|
+ result = ((ResponseMessage) result).getResult();
|
|
|
+ }
|
|
|
+ if (result instanceof PagerResult) {
|
|
|
+ result = ((PagerResult) result).getData();
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
}
|