From 133fd1ab28069e99c1cc1eac936649d19c82e927 Mon Sep 17 00:00:00 2001 From: zhou-hao Date: Tue, 28 Nov 2017 19:05:37 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=9D=83=E9=99=90=E6=8E=A7?= =?UTF-8?q?=E5=88=B6,=E5=A2=9E=E5=8A=A0=E6=B5=8B=E8=AF=95=E7=94=A8?= =?UTF-8?q?=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hsweb-authorization-basic/pom.xml | 6 + .../basic/aop/AopAuthorizingController.java | 28 ++- .../DefaultBasicAuthorizeDefinition.java | 95 ++------ .../define/EmptyAuthorizeDefinition.java | 6 + .../handler/DefaultAuthorizingHandler.java | 1 - .../basic/handler/MethodProceedCallback.java | 5 + .../access/DefaultDataAccessController.java | 11 +- .../access/FieldFilterDataAccessHandler.java | 51 ++++- .../access/FieldScopeDataAccessHandler.java | 71 ++++-- .../handler/access/InvokeResultUtils.java | 16 ++ .../access/OwnCreatedDataAccessHandler.java | 88 ++++--- .../access/ScriptDataAccessHandler.java | 2 - .../AuthorizeDefinitionTests.java | 215 ++++++++++++++++++ 13 files changed, 444 insertions(+), 151 deletions(-) create mode 100644 hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/MethodProceedCallback.java create mode 100644 hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/access/InvokeResultUtils.java create mode 100644 hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/AuthorizeDefinitionTests.java diff --git a/hsweb-authorization/hsweb-authorization-basic/pom.xml b/hsweb-authorization/hsweb-authorization-basic/pom.xml index e6f8108e9..e66f5c657 100644 --- a/hsweb-authorization/hsweb-authorization-basic/pom.xml +++ b/hsweb-authorization/hsweb-authorization-basic/pom.xml @@ -65,6 +65,12 @@ redisson test + + org.mockito + mockito-all + 1.10.19 + test + \ No newline at end of file diff --git a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/aop/AopAuthorizingController.java b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/aop/AopAuthorizingController.java index bed5917c1..4a16889d7 100644 --- a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/aop/AopAuthorizingController.java +++ b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/aop/AopAuthorizingController.java @@ -7,20 +7,26 @@ import org.hswebframework.web.authorization.annotation.Authorize; import org.hswebframework.web.authorization.define.AuthorizingContext; import org.hswebframework.web.authorization.basic.handler.AuthorizingHandler; import org.hswebframework.web.authorization.define.AuthorizeDefinition; +import org.hswebframework.web.authorization.define.Phased; import org.hswebframework.web.authorization.exception.UnAuthorizedException; import org.hswebframework.web.boost.aop.context.MethodInterceptorHolder; import org.hswebframework.web.boost.aop.context.MethodInterceptorContext; +import org.hswebframework.web.controller.message.ResponseMessage; import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RestController; import java.lang.reflect.Method; +import java.util.Map; /** * @author zhouhao */ public class AopAuthorizingController extends StaticMethodMatcherPointcutAdvisor { + private static final long serialVersionUID = 1154190623020670672L; + public AopAuthorizingController(AuthorizingHandler authorizingHandler, AopMethodAuthorizeDefinitionParser aopMethodAuthorizeDefinitionParser) { super((MethodInterceptor) methodInvocation -> { @@ -29,22 +35,36 @@ public class AopAuthorizingController extends StaticMethodMatcherPointcutAdvisor MethodInterceptorContext paramContext = holder.createParamContext(); AuthorizeDefinition definition = aopMethodAuthorizeDefinitionParser.parse(paramContext); - + Object result = true; + boolean isControl = false; if (null != definition) { Authentication authentication = Authentication.current().orElseThrow(UnAuthorizedException::new); - if (!definition.isEmpty()) { AuthorizingContext context = new AuthorizingContext(); context.setAuthentication(authentication); context.setDefinition(definition); context.setParamContext(paramContext); - authorizingHandler.handle(context); + isControl = true; + if (definition.getPhased() == Phased.before) { + authorizingHandler.handle(context); + result = methodInvocation.proceed(); + } else { + result = methodInvocation.proceed(); + context.setParamContext(holder.createParamContext(result)); + authorizingHandler.handle(context); + } } } - return methodInvocation.proceed(); + if (!isControl) { + result = methodInvocation.proceed(); + } + + return result; }); } + + @Override public boolean matches(Method method, Class aClass) { //对controller进行控制 diff --git a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/define/DefaultBasicAuthorizeDefinition.java b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/define/DefaultBasicAuthorizeDefinition.java index ed29ae94e..733c846e9 100644 --- a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/define/DefaultBasicAuthorizeDefinition.java +++ b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/define/DefaultBasicAuthorizeDefinition.java @@ -1,5 +1,6 @@ package org.hswebframework.web.authorization.basic.define; +import lombok.*; import org.hswebframework.web.authorization.access.DataAccessController; import org.hswebframework.web.authorization.annotation.Authorize; import org.hswebframework.web.authorization.annotation.Logical; @@ -7,6 +8,7 @@ import org.hswebframework.web.authorization.annotation.RequiresDataAccess; import org.hswebframework.web.authorization.annotation.RequiresExpression; import org.hswebframework.web.authorization.define.AuthorizeDefinition; import org.hswebframework.web.authorization.define.DataAccessDefinition; +import org.hswebframework.web.authorization.define.Phased; import org.hswebframework.web.authorization.define.Script; import java.util.Arrays; @@ -19,6 +21,10 @@ import java.util.Set; * @author zhouhao * @since 3.0 */ +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor public class DefaultBasicAuthorizeDefinition implements AuthorizeDefinition { private boolean dataAccessControl; @@ -38,97 +44,23 @@ public class DefaultBasicAuthorizeDefinition implements AuthorizeDefinition { private DataAccessDefinition dataAccessDefinition; + private Phased phased = Phased.before; + + @Override + public Phased getPhased() { + return phased; + } + @Override public int getPriority() { return Integer.MIN_VALUE; } - @Override - public boolean isDataAccessControl() { - return dataAccessControl; - } - - @Override - public Set getPermissions() { - return new HashSet<>(permissions); - } - - @Override - public Set getActions() { - return new HashSet<>(actions); - } - - @Override - public Set getRoles() { - return new HashSet<>(roles); - } - - @Override - public Set getUser() { - return new HashSet<>(user); - } - - @Override - public Script getScript() { - return script; - } - - @Override - public String getMessage() { - return message; - } - - @Override - public Logical getLogical() { - return logical; - } - @Override public boolean isEmpty() { return permissions.isEmpty() && roles.isEmpty() && user.isEmpty() && script == null && dataAccessDefinition == null; } - @Override - public DataAccessDefinition getDataAccessDefinition() { - return dataAccessDefinition; - } - - public void setDataAccessDefinition(DataAccessDefinition dataAccessDefinition) { - this.dataAccessDefinition = dataAccessDefinition; - } - - public void setActions(Set actions) { - this.actions = actions; - } - - public void setDataAccessControl(boolean dataAccessControl) { - this.dataAccessControl = dataAccessControl; - } - - public void setLogical(Logical logical) { - this.logical = logical; - } - - public void setMessage(String message) { - this.message = message; - } - - public void setPermissions(Set permissions) { - this.permissions = permissions; - } - - public void setRoles(Set roles) { - this.roles = roles; - } - - public void setScript(Script script) { - this.script = script; - } - - public void setUser(Set user) { - this.user = user; - } - public void put(Authorize authorize) { if (null == authorize || authorize.ignore()) { return; @@ -141,6 +73,7 @@ public class DefaultBasicAuthorizeDefinition implements AuthorizeDefinition { logical = authorize.logical(); } message = authorize.message(); + phased=authorize.phased(); } public void put(RequiresExpression expression) { diff --git a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/define/EmptyAuthorizeDefinition.java b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/define/EmptyAuthorizeDefinition.java index 844097605..bd73e1516 100644 --- a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/define/EmptyAuthorizeDefinition.java +++ b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/define/EmptyAuthorizeDefinition.java @@ -3,6 +3,7 @@ package org.hswebframework.web.authorization.basic.define; import org.hswebframework.web.authorization.annotation.Logical; import org.hswebframework.web.authorization.define.AuthorizeDefinition; import org.hswebframework.web.authorization.define.DataAccessDefinition; +import org.hswebframework.web.authorization.define.Phased; import org.hswebframework.web.authorization.define.Script; import java.util.Set; @@ -17,6 +18,11 @@ public class EmptyAuthorizeDefinition implements AuthorizeDefinition { private EmptyAuthorizeDefinition() { } + @Override + public Phased getPhased() { + throw new UnsupportedOperationException(); + } + @Override public int getPriority() { throw new UnsupportedOperationException(); diff --git a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/DefaultAuthorizingHandler.java b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/DefaultAuthorizingHandler.java index e4079481d..aaf998634 100644 --- a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/DefaultAuthorizingHandler.java +++ b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/DefaultAuthorizingHandler.java @@ -52,7 +52,6 @@ public class DefaultAuthorizingHandler implements AuthorizingHandler { //表达式权限控制 handleExpression(context.getAuthentication(), context.getDefinition(), context.getParamContext()); - } protected void handleDataAccess(AuthorizingContext context) { diff --git a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/MethodProceedCallback.java b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/MethodProceedCallback.java new file mode 100644 index 000000000..4ae449737 --- /dev/null +++ b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/MethodProceedCallback.java @@ -0,0 +1,5 @@ +package org.hswebframework.web.authorization.basic.handler; + +public interface MethodProceedCallback { + void call(Object result); +} diff --git a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/access/DefaultDataAccessController.java b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/access/DefaultDataAccessController.java index ad16c6a1b..d9308224a 100644 --- a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/access/DefaultDataAccessController.java +++ b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/access/DefaultDataAccessController.java @@ -30,11 +30,11 @@ public final class DefaultDataAccessController implements DataAccessController { throw new UnsupportedOperationException(); } this.parent = parent; - addHandler(new CustomDataAccessHandler()); - addHandler(new OwnCreatedDataAccessHandler()); - addHandler(new ScriptDataAccessHandler()); - addHandler(new FieldFilterDataAccessHandler()); - addHandler(new FieldScopeDataAccessHandler()); + addHandler(new CustomDataAccessHandler()). + addHandler(new OwnCreatedDataAccessHandler()). + addHandler(new ScriptDataAccessHandler()). + addHandler(new FieldFilterDataAccessHandler()). + addHandler(new FieldScopeDataAccessHandler()); } @Override @@ -43,7 +43,6 @@ public final class DefaultDataAccessController implements DataAccessController { parent.doAccess(access, context); } return handlers.stream() - // TODO: 17-3-28 可以换成access对应的handler以提高效率 .filter(handler -> handler.isSupport(access)) .allMatch(handler -> handler.handle(access, context)); } diff --git a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/access/FieldFilterDataAccessHandler.java b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/access/FieldFilterDataAccessHandler.java index 50b8e3eb0..602aa5cd2 100644 --- a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/access/FieldFilterDataAccessHandler.java +++ b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/access/FieldFilterDataAccessHandler.java @@ -6,13 +6,19 @@ import org.hswebframework.web.authorization.access.DataAccessConfig; import org.hswebframework.web.authorization.access.DataAccessHandler; import org.hswebframework.web.authorization.access.FieldFilterDataAccessConfig; import org.hswebframework.web.authorization.define.AuthorizingContext; +import org.hswebframework.web.authorization.define.Phased; import org.hswebframework.web.commons.entity.Entity; import org.hswebframework.web.commons.entity.param.QueryParamEntity; import org.hswebframework.web.commons.model.Model; +import org.hswebframework.web.controller.message.ResponseMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.http.ResponseEntity; +import java.lang.reflect.InvocationTargetException; +import java.util.Collection; import java.util.Map; +import java.util.Set; /** * 数据权限字段过滤处理,目前仅支持deny. {@link DataAccessConfig.DefaultType#DENY_FIELDS} @@ -33,6 +39,7 @@ public class FieldFilterDataAccessHandler implements DataAccessHandler { switch (access.getAction()) { case Permission.ACTION_QUERY: + case Permission.ACTION_GET: return doQueryAccess(filterDataAccessConfig, context); case Permission.ACTION_UPDATE: return doUpdateAccess(filterDataAccessConfig, context); @@ -53,7 +60,7 @@ public class FieldFilterDataAccessHandler implements DataAccessHandler { */ protected boolean doUpdateAccess(FieldFilterDataAccessConfig accesses, AuthorizingContext params) { Object supportParam = params.getParamContext().getParams().values().stream() - .filter(param -> (param instanceof Entity) || (param instanceof Model)||(param instanceof Map)) + .filter(param -> (param instanceof Entity) || (param instanceof Model) || (param instanceof Map)) .findAny() .orElse(null); if (null != supportParam) { @@ -73,18 +80,40 @@ public class FieldFilterDataAccessHandler implements DataAccessHandler { return true; } - + @SuppressWarnings("all") protected boolean doQueryAccess(FieldFilterDataAccessConfig access, AuthorizingContext context) { - QueryParamEntity entity = context.getParamContext().getParams() - .values().stream() - .filter(QueryParamEntity.class::isInstance) - .map(QueryParamEntity.class::cast) - .findAny().orElse(null); - if (entity == null) { - logger.warn("try validate query access, but query entity is null or not instance of org.hswebframework.web.commons.entity.Entity"); - return true; + if (context.getDefinition().getPhased() == Phased.before) { + QueryParamEntity entity = context.getParamContext().getParams() + .values().stream() + .filter(QueryParamEntity.class::isInstance) + .map(QueryParamEntity.class::cast) + .findAny().orElse(null); + if (entity == null) { + logger.warn("try validate query access, but query entity is null or not instance of org.hswebframework.web.commons.entity.Entity"); + return true; + } + entity.excludes(access.getFields().toArray(new String[access.getFields().size()])); + } else { + Object result = InvokeResultUtils.convertRealResult(context.getParamContext().getInvokeResult()); + if (result instanceof Collection) { + ((Collection) result).forEach(o -> setObjectPropertyNull(o, access.getFields())); + } else { + setObjectPropertyNull(result, access.getFields()); + } } - entity.excludes(access.getFields().toArray(new String[access.getFields().size()])); return true; } + + protected void setObjectPropertyNull(Object obj, Set fields) { + if (null == obj) { + return; + } + for (String field : fields) { + try { + BeanUtilsBean.getInstance().getPropertyUtils().setProperty(obj, field, null); + } catch (Exception ignore) { + + } + } + } } diff --git a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/access/FieldScopeDataAccessHandler.java b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/access/FieldScopeDataAccessHandler.java index 87988a0aa..4131da265 100644 --- a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/access/FieldScopeDataAccessHandler.java +++ b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/access/FieldScopeDataAccessHandler.java @@ -9,14 +9,18 @@ import org.hswebframework.web.authorization.access.DataAccessConfig; import org.hswebframework.web.authorization.access.DataAccessHandler; import org.hswebframework.web.authorization.access.FieldScopeDataAccessConfig; import org.hswebframework.web.authorization.define.AuthorizingContext; +import org.hswebframework.web.authorization.define.Phased; import org.hswebframework.web.commons.entity.param.QueryParamEntity; import org.hswebframework.web.controller.QueryController; import org.hswebframework.web.service.QueryService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; +import java.util.Collection; import java.util.List; +import java.util.Set; /** * @author zhouhao @@ -38,8 +42,8 @@ public class FieldScopeDataAccessHandler implements DataAccessHandler { if (controller != null) { switch (access.getAction()) { case Permission.ACTION_QUERY: - return doQueryAccess(own, context); case Permission.ACTION_GET: + return doQueryAccess(own, context); case Permission.ACTION_DELETE: case Permission.ACTION_UPDATE: return doRWAccess(own, context, controller); @@ -78,29 +82,58 @@ public class FieldScopeDataAccessHandler implements DataAccessHandler { } + @SuppressWarnings("all") protected boolean doQueryAccess(FieldScopeDataAccessConfig access, AuthorizingContext context) { - QueryParamEntity entity = context.getParamContext().getParams() - .values().stream() - .filter(QueryParamEntity.class::isInstance) - .map(QueryParamEntity.class::cast) - .findAny().orElse(null); - if (entity == null) { - logger.warn("try validate query access, but query entity is null or not instance of org.hswebframework.web.commons.entity.Entity"); - return true; + if (context.getDefinition().getPhased() == Phased.before) { + QueryParamEntity entity = context.getParamContext().getParams() + .values().stream() + .filter(QueryParamEntity.class::isInstance) + .map(QueryParamEntity.class::cast) + .findAny().orElse(null); + if (entity == null) { + logger.warn("try validate query access, but query entity is null or not instance of org.hswebframework.web.commons.entity.Entity"); + return true; + } + //重构查询条件 + //如: 旧的条件为 where column =? or column = ? + //重构后为: where creatorId=? and (column = ? or column = ?) + List oldParam = entity.getTerms(); + //清空旧的查询条件 + entity.setTerms(new ArrayList<>()); + //添加一个查询条件 + entity.addTerm(createQueryTerm(access)) + //客户端提交的参数 作为嵌套参数 + .nest().setTerms(oldParam); + } else { + Object result = InvokeResultUtils.convertRealResult(context.getParamContext().getInvokeResult()); + if (result == null) { + return true; + } + if (result instanceof Collection) { + return ((Collection) result).stream().allMatch(obj -> propertyInScope(obj, access.getField(), access.getScope())); + } else { + return propertyInScope(result, access.getField(), access.getScope()); + } } - //重构查询条件 - //如: 旧的条件为 where column =? or column = ? - //重构后为: where creatorId=? and (column = ? or column = ?) - List oldParam = entity.getTerms(); - //清空旧的查询条件 - entity.setTerms(new ArrayList<>()); - //添加一个查询条件 - entity.addTerm(createQueryTerm(access)) - //客户端提交的参数 作为嵌套参数 - .nest().setTerms(oldParam); return true; } + protected boolean propertyInScope(Object obj, String property, Set scope) { + if (null == obj) { + return false; + } + try { + Object value = BeanUtilsBean.getInstance().getProperty(obj, property); + if (null != value) { + return scope.contains(value); + } + } catch (Exception ignore) { + logger.warn("can not get property {} from {},{}", property, obj, ignore.getMessage()); + } + return true; + + } + protected Term createQueryTerm(FieldScopeDataAccessConfig access) { Term term = new Term(); term.setType(Term.Type.and); diff --git a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/access/InvokeResultUtils.java b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/access/InvokeResultUtils.java new file mode 100644 index 000000000..b79bd9ead --- /dev/null +++ b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/access/InvokeResultUtils.java @@ -0,0 +1,16 @@ +package org.hswebframework.web.authorization.basic.handler.access; + +import org.hswebframework.web.controller.message.ResponseMessage; +import org.springframework.http.ResponseEntity; + +public class InvokeResultUtils { + public static Object convertRealResult(Object result) { + if (result instanceof ResponseMessage) { + return ((ResponseMessage) result).getResult(); + } + if (result instanceof ResponseEntity) { + return ((ResponseEntity) result).getBody(); + } + return result; + } +} diff --git a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/access/OwnCreatedDataAccessHandler.java b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/access/OwnCreatedDataAccessHandler.java index 8cf63fb2b..abe15f47d 100644 --- a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/access/OwnCreatedDataAccessHandler.java +++ b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/access/OwnCreatedDataAccessHandler.java @@ -1,5 +1,7 @@ package org.hswebframework.web.authorization.basic.handler.access; +import org.apache.commons.beanutils.PropertyUtils; +import org.apache.commons.beanutils.PropertyUtilsBean; import org.hswebframework.ezorm.core.param.Term; import org.hswebframework.utils.ClassUtils; import org.hswebframework.web.authorization.Permission; @@ -7,6 +9,7 @@ import org.hswebframework.web.authorization.access.DataAccessConfig; import org.hswebframework.web.authorization.access.DataAccessHandler; import org.hswebframework.web.authorization.access.OwnCreatedDataAccessConfig; import org.hswebframework.web.authorization.define.AuthorizingContext; +import org.hswebframework.web.authorization.define.Phased; import org.hswebframework.web.commons.entity.Entity; import org.hswebframework.web.commons.entity.RecordCreationEntity; import org.hswebframework.web.commons.entity.param.QueryParamEntity; @@ -15,12 +18,14 @@ import org.hswebframework.web.service.QueryService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; /** - * TODO 完成注释 - * * @author zhouhao */ public class OwnCreatedDataAccessHandler implements DataAccessHandler { @@ -37,12 +42,13 @@ public class OwnCreatedDataAccessHandler implements DataAccessHandler { Object controller = context.getParamContext().getTarget(); if (controller != null) { switch (access.getAction()) { + case Permission.ACTION_GET: case Permission.ACTION_QUERY: return doQueryAccess(own, context); - case Permission.ACTION_GET: + case Permission.ACTION_DELETE: case Permission.ACTION_UPDATE: - return doRWAccess(own, context,controller); + return doRWAccess(own, context, controller); case Permission.ACTION_ADD: //put creator_id to result return putCreatorId(own, context); @@ -91,32 +97,60 @@ public class OwnCreatedDataAccessHandler implements DataAccessHandler { } protected boolean doQueryAccess(OwnCreatedDataAccessConfig access, AuthorizingContext context) { - Entity entity = context.getParamContext().getParams() - .values().stream() - .filter(Entity.class::isInstance) - .map(Entity.class::cast) - .findAny().orElse(null); - if (entity == null) { - logger.warn("try validate query access, but query entity is null or not instance of org.hswebframework.web.commons.entity.Entity"); + String userId = context.getAuthentication().getUser().getId(); + + if (context.getDefinition().getPhased() == Phased.before) { + Entity entity = context.getParamContext().getParams() + .values().stream() + .filter(Entity.class::isInstance) + .map(Entity.class::cast) + .findAny().orElse(null); + if (entity == null) { + logger.warn("try validate query access, but query entity is null or not instance of org.hswebframework.web.commons.entity.Entity"); + return true; + } + if (entity instanceof QueryParamEntity) { + QueryParamEntity queryParamEntity = ((QueryParamEntity) entity); + //重构查询条件 + //如: 旧的条件为 where name =? or name = ? + //重构后为: where creatorId=? and (name = ? or name = ?) + List oldParam = queryParamEntity.getTerms(); + //清空旧的查询条件 + queryParamEntity.setTerms(new ArrayList<>()); + //添加一个查询条件 + queryParamEntity + .where(RecordCreationEntity.creatorId, userId) + //客户端提交的参数 作为嵌套参数 + .nest().setTerms(oldParam); + } else if (entity instanceof RecordCreationEntity) { + ((RecordCreationEntity) entity).setCreatorId(userId); + } else { + logger.warn("try validate query access,but entity not support, QueryParamEntity and RecordCreationEntity support now!"); + } + } else { + Object result = InvokeResultUtils.convertRealResult(context.getParamContext().getInvokeResult()); + return matchCreatorId(result, userId); + } + return true; + } + + @SuppressWarnings("all") + protected boolean matchCreatorId(Object result, String userId) { + if (null == result) { return true; } - if (entity instanceof QueryParamEntity) { - QueryParamEntity queryParamEntity = ((QueryParamEntity) entity); - //重构查询条件 - //如: 旧的条件为 where name =? or name = ? - //重构后为: where creatorId=? and (name = ? or name = ?) - List oldParam = queryParamEntity.getTerms(); - //清空旧的查询条件 - queryParamEntity.setTerms(new ArrayList<>()); - //添加一个查询条件 - queryParamEntity - .where(RecordCreationEntity.creatorId,context.getAuthentication().getUser().getId()) - //客户端提交的参数 作为嵌套参数 - .nest().setTerms(oldParam); - } else if (entity instanceof RecordCreationEntity) { - ((RecordCreationEntity) entity).setCreatorId(context.getAuthentication().getUser().getId()); + if (result instanceof RecordCreationEntity) { + return userId.equals(((RecordCreationEntity) result).getCreatorId()); + } else if (result instanceof Collection) { + Collection collection = ((Collection) result); + //删掉不能访问的对象 + collection.removeAll(collection.stream().filter((Object o) -> !matchCreatorId(o, userId)) + .collect(Collectors.toList())); } else { - logger.warn("try validate query access,but entity not support, QueryParamEntity and RecordCreationEntity support now!"); + try { + return userId.equals(PropertyUtils.getProperty(result, "creatorId")); + } catch (Exception ignore) { + } } return true; } diff --git a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/access/ScriptDataAccessHandler.java b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/access/ScriptDataAccessHandler.java index 4ccf7352e..5fc5880a0 100644 --- a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/access/ScriptDataAccessHandler.java +++ b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/access/ScriptDataAccessHandler.java @@ -11,8 +11,6 @@ import org.hswebframework.web.authorization.access.ScriptDataAccessConfig; import org.hswebframework.web.authorization.define.AuthorizingContext; /** - * TODO 完成注释 - * * @author zhouhao */ public class ScriptDataAccessHandler implements DataAccessHandler { diff --git a/hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/AuthorizeDefinitionTests.java b/hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/AuthorizeDefinitionTests.java new file mode 100644 index 000000000..33cab9abb --- /dev/null +++ b/hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/AuthorizeDefinitionTests.java @@ -0,0 +1,215 @@ +package org.hswebframework.web.authorization; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.serializer.SerializerFeature; +import lombok.*; +import org.hswebframework.web.authorization.annotation.Authorize; +import org.hswebframework.web.authorization.annotation.RequiresDataAccess; +import org.hswebframework.web.authorization.basic.aop.AopMethodAuthorizeDefinitionParser; +import org.hswebframework.web.authorization.basic.aop.DefaultAopMethodAuthorizeDefinitionParser; +import org.hswebframework.web.authorization.basic.handler.DefaultAuthorizingHandler; +import org.hswebframework.web.authorization.basic.handler.access.DefaultDataAccessController; +import org.hswebframework.web.authorization.define.AuthorizeDefinition; +import org.hswebframework.web.authorization.define.AuthorizingContext; +import org.hswebframework.web.authorization.define.Phased; +import org.hswebframework.web.authorization.simple.*; +import org.hswebframework.web.boost.aop.context.MethodInterceptorContext; +import org.hswebframework.web.commons.entity.param.QueryParamEntity; +import org.hswebframework.web.controller.message.ResponseMessage; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +import java.util.*; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class AuthorizeDefinitionTests { + + @Mock + private MethodInterceptorContext queryById; + @Mock + private MethodInterceptorContext dynamicQuery; + + @Mock + private Authentication authentication; + + AopMethodAuthorizeDefinitionParser parser = new DefaultAopMethodAuthorizeDefinitionParser(); + + + @Before + public void init() throws NoSuchMethodException { + TestClass testClass = new TestClass(); + + QueryParamEntity entity = new QueryParamEntity(); + entity.where("id", "admin").or("name", "admin"); + + User user = User.builder().name("test").id("test") + .orgId("400000") + .password("admin").salt("1234").build(); + + //mock MethodInterceptorContext + when(queryById.getMethod()).thenReturn(TestClass.class.getMethod("queryById", String.class)); + when(queryById.getTarget()).thenReturn(testClass); + when(queryById.getParameter("id")).thenReturn(Optional.of("test")); + when(queryById.getParams()).thenReturn(Collections.singletonMap("id", "test")); + when(queryById.getInvokeResult()).thenReturn(ResponseMessage.ok(user)); + + + //mock MethodInterceptorContext + when(dynamicQuery.getMethod()).thenReturn(TestClass.class.getMethod("dynamicQuery", QueryParamEntity.class)); + when(dynamicQuery.getTarget()).thenReturn(testClass); + when(dynamicQuery.getParams()).thenReturn(Collections.singletonMap("paramEntity", entity)); + when(dynamicQuery.getParameter("paramEntity")).thenReturn(Optional.of(entity)); + + + //过滤字段 + AbstractDataAccessConfig fieldFilter = new SimpleFieldFilterDataAccessConfig("password", "salt"); + fieldFilter.setAction(Permission.ACTION_QUERY); + + SimpleFiledScopeDataAccessConfig filedScope = new SimpleFiledScopeDataAccessConfig(); + filedScope.setAction(Permission.ACTION_QUERY); + filedScope.setField("orgId"); + filedScope.setScopeType("org"); + filedScope.setScope(Collections.singleton("400000")); + + //mock authentication + when(authentication.getUser()).thenReturn(SimpleUser.builder().id("admin").name("admin").build()); + when(authentication.getPermissions()).thenReturn(Arrays.asList(SimplePermission.builder() + .id("test") + .dataAccesses(new HashSet<>(Arrays.asList(fieldFilter, filedScope))) + + .actions(new HashSet<>(Arrays.asList(Permission.ACTION_QUERY, Permission.ACTION_UPDATE))).build())); + + } + + + @Test + public void testParseAuthorizeDefinition() { + AuthorizeDefinition definition = parser.parse(queryById); + + Assert.assertNotNull(definition); + Assert.assertEquals(definition.getPermissions().size(), 1); + Assert.assertEquals(definition.getPermissions().iterator().next(), "test"); + Assert.assertEquals(definition.getActions().iterator().next(), Permission.ACTION_QUERY); + } + + @Test + public void testAuthorizingHandler() { + DefaultAuthorizingHandler handler = new DefaultAuthorizingHandler(); + + AuthorizeDefinition definition = parser.parse(queryById); + + AuthorizingContext authorizingContext = new AuthorizingContext(); + authorizingContext.setAuthentication(authentication); + authorizingContext.setDefinition(definition); + authorizingContext.setParamContext(queryById); + + handler.handle(authorizingContext); + + + } + + /** + * 测试数据权限控制s + */ + @Test + public void testDynamicQueryDataAccessHandler() { + + DefaultAuthorizingHandler handler = new DefaultAuthorizingHandler(); + DefaultDataAccessController controller = new DefaultDataAccessController(); + handler.setDataAccessController(controller); + + + AuthorizeDefinition definition = parser.parse(dynamicQuery); + + //获取到请求参数 + QueryParamEntity entity = dynamicQuery.getParameter("paramEntity").orElseThrow(NullPointerException::new); + System.out.println(JSON.toJSONString(entity, SerializerFeature.PrettyFormat)); + + AuthorizingContext authorizingContext = new AuthorizingContext(); + authorizingContext.setAuthentication(authentication); + authorizingContext.setDefinition(definition); + authorizingContext.setParamContext(dynamicQuery); + + handler.handle(authorizingContext); + + System.out.println(JSON.toJSONString(entity, SerializerFeature.PrettyFormat)); + + Assert.assertTrue(entity.getExcludes().size() == 2); + Assert.assertTrue(entity.getTerms().size() == 2); + Assert.assertTrue(entity.getTerms().get(1).getTerms().size() == 2); + } + + /** + * 测试数据权限控制s + */ + @Test + public void testGetDataAccessHandler() { + + DefaultAuthorizingHandler handler = new DefaultAuthorizingHandler(); + DefaultDataAccessController controller = new DefaultDataAccessController(); + handler.setDataAccessController(controller); + + + AuthorizeDefinition definition = parser.parse(queryById); + + //响应结果 + Object response = queryById.getInvokeResult(); + + System.out.println(JSON.toJSONString(response, SerializerFeature.PrettyFormat)); + + AuthorizingContext authorizingContext = new AuthorizingContext(); + authorizingContext.setAuthentication(authentication); + authorizingContext.setDefinition(definition); + authorizingContext.setParamContext(queryById); + + handler.handle(authorizingContext); + + System.out.println(JSON.toJSONString(response, SerializerFeature.PrettyFormat)); + Assert.assertTrue(response instanceof ResponseMessage); + Assert.assertTrue(((User) ((ResponseMessage) response).getResult()).getPassword() == null); + Assert.assertTrue(((User) ((ResponseMessage) response).getResult()).getSalt() == null); + } + + @Authorize(permission = "test") + public static class TestClass { + + @Authorize(action = Permission.ACTION_QUERY, phased = Phased.after, dataAccess = @RequiresDataAccess) + public ResponseMessage queryById(String id) { + return ResponseMessage.ok(); + } + + @Authorize(action = Permission.ACTION_QUERY) + @RequiresDataAccess + public void dynamicQuery(QueryParamEntity paramEntity) { + System.out.println(JSON.toJSON(paramEntity)); + } + + } + + @Getter + @Setter + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class User { + private String id; + + private String name; + + private String password; + + private String salt; + + private String orgId; + + } +}