From 9af9811f5798bdc12557fc9f4989bf12cc4ea5f2 Mon Sep 17 00:00:00 2001 From: zhouhao Date: Tue, 25 Feb 2020 14:27:32 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=9D=83=E9=99=90=E8=A7=A3?= =?UTF-8?q?=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/authorization/Authentication.java | 9 + .../ReactiveAuthenticationHolder.java | 12 +- .../annotation/DataAccessType.java | 1 + .../annotation/DimensionDataAccess.java | 5 +- .../annotation/FieldDataAccess.java | 6 +- .../authorization/annotation/UserOwnData.java | 1 - .../define/DataAccessDefinition.java | 6 +- .../define/AopAuthorizeDefinitionParser.java | 155 ++++++++++++++++++ .../DefaultBasicAuthorizeDefinition.java | 105 ++---------- .../aop/AopAuthorizingControllerTest.java | 7 +- .../basic/aop/TestController.java | 1 + .../DefaultBasicAuthorizeDefinitionTest.java | 2 +- 12 files changed, 206 insertions(+), 104 deletions(-) create mode 100644 hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/define/AopAuthorizeDefinitionParser.java diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/Authentication.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/Authentication.java index 976909412..ebb5b5b3d 100644 --- a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/Authentication.java +++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/Authentication.java @@ -30,6 +30,7 @@ import java.util.stream.Collectors; * * * @author zhouhao @@ -51,6 +52,7 @@ public interface Authentication extends Serializable { * * @return 当前用户权限信息 * @see ReactiveAuthenticationHolder + * @since 4.0 */ static Mono currentReactive() { return ReactiveAuthenticationHolder.get(); @@ -81,6 +83,7 @@ public interface Authentication extends Serializable { /** * @return 用户所有维度 + * @since 4.0 */ List getDimensions(); @@ -196,6 +199,12 @@ public interface Authentication extends Serializable { */ Map getAttributes(); + /** + * 合并权限 + * + * @param source 源权限信息 + * @return 合并后的信息 + */ Authentication merge(Authentication source); } diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/ReactiveAuthenticationHolder.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/ReactiveAuthenticationHolder.java index e1f929712..bd8dfa038 100644 --- a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/ReactiveAuthenticationHolder.java +++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/ReactiveAuthenticationHolder.java @@ -42,7 +42,7 @@ import java.util.stream.Collectors; * * @author zhouhao * @see ReactiveAuthenticationSupplier - * @since 3.0 + * @since 4.0 */ public final class ReactiveAuthenticationHolder { private static final List suppliers = new ArrayList<>(); @@ -90,4 +90,14 @@ public final class ReactiveAuthenticationHolder { } } + public static void setSupplier(ReactiveAuthenticationSupplier supplier){ + lock.writeLock().lock(); + try { + suppliers.clear(); + suppliers.add(supplier); + } finally { + lock.writeLock().unlock(); + } + } + } diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/annotation/DataAccessType.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/annotation/DataAccessType.java index 33d8bc093..675e3d911 100644 --- a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/annotation/DataAccessType.java +++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/annotation/DataAccessType.java @@ -24,4 +24,5 @@ public @interface DataAccessType { Class configuration() default DataAccessConfiguration.class; + boolean ignore() default false; } \ No newline at end of file diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/annotation/DimensionDataAccess.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/annotation/DimensionDataAccess.java index fe4e4f0bd..6a90247b2 100644 --- a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/annotation/DimensionDataAccess.java +++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/annotation/DimensionDataAccess.java @@ -15,7 +15,10 @@ public @interface DimensionDataAccess { Mapping[] mapping() default {}; @AliasFor(annotation = Authorize.class) - Phased phased() default Phased.before; + Phased phased() default Phased.before; + + @AliasFor(annotation = DataAccessType.class) + boolean ignore() default false; @Retention(RetentionPolicy.RUNTIME) @Documented diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/annotation/FieldDataAccess.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/annotation/FieldDataAccess.java index cc194004d..5c945f3fc 100644 --- a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/annotation/FieldDataAccess.java +++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/annotation/FieldDataAccess.java @@ -1,11 +1,15 @@ package org.hswebframework.web.authorization.annotation; +import org.springframework.core.annotation.AliasFor; + import java.lang.annotation.*; -@DataAccessType(id = "DENY_FIELDS", name = "字段权限") +@DataAccessType(id = "FIELD_DENY", name = "字段权限") @Retention(RetentionPolicy.RUNTIME) @Documented @Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD}) public @interface FieldDataAccess { + @AliasFor(annotation = DataAccessType.class) + boolean ignore() default false; } diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/annotation/UserOwnData.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/annotation/UserOwnData.java index ad6e4235a..7f4caa9f8 100644 --- a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/annotation/UserOwnData.java +++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/annotation/UserOwnData.java @@ -9,7 +9,6 @@ import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented -@DataAccess @DataAccessType(id = "user_own_data", name = "用户自己的数据") public @interface UserOwnData { diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/define/DataAccessDefinition.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/define/DataAccessDefinition.java index 7281b0937..f30135276 100644 --- a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/define/DataAccessDefinition.java +++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/define/DataAccessDefinition.java @@ -9,12 +9,12 @@ import java.util.*; @Setter public class DataAccessDefinition { - Set dataAccessTypes=new HashSet<>(); + Set dataAccessTypes = new HashSet<>(); - public Optional getType(String typeId){ + public Optional getType(String typeId) { return dataAccessTypes .stream() - .filter(type->type.getId().equalsIgnoreCase(typeId)) + .filter(type -> type.getId() != null && type.getId().equalsIgnoreCase(typeId)) .findAny(); } } diff --git a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/define/AopAuthorizeDefinitionParser.java b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/define/AopAuthorizeDefinitionParser.java new file mode 100644 index 000000000..db3aa39f1 --- /dev/null +++ b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/define/AopAuthorizeDefinitionParser.java @@ -0,0 +1,155 @@ +package org.hswebframework.web.authorization.basic.define; + +import org.hswebframework.web.authorization.annotation.*; +import org.hswebframework.web.authorization.define.AopAuthorizeDefinition; +import org.hswebframework.web.authorization.define.ResourceActionDefinition; +import org.hswebframework.web.authorization.define.ResourceDefinition; +import org.springframework.core.annotation.AnnotatedElementUtils; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class AopAuthorizeDefinitionParser { + + private static final Set> types = new HashSet<>(Arrays.asList( + Authorize.class, + DataAccess.class, + Dimension.class, + Resource.class, + ResourceAction.class, + DataAccessType.class + )); + + private Set methodAnnotation; + + private Set classAnnotation; + + private Map, List> classAnnotationGroup; + + private Map, List> methodAnnotationGroup; + + private DefaultBasicAuthorizeDefinition definition; + + AopAuthorizeDefinitionParser(Class targetClass, Method method) { + definition = new DefaultBasicAuthorizeDefinition(); + definition.setTargetClass(targetClass); + definition.setTargetMethod(method); + + methodAnnotation = AnnotatedElementUtils.findAllMergedAnnotations(method, types); + + classAnnotation = AnnotatedElementUtils.findAllMergedAnnotations(targetClass, types); + + classAnnotationGroup = classAnnotation + .stream() + .collect(Collectors.groupingBy(Annotation::annotationType)); + + methodAnnotationGroup = methodAnnotation + .stream() + .collect(Collectors.groupingBy(Annotation::annotationType)); + } + + private void initClassAnnotation() { + for (Annotation annotation : classAnnotation) { + if (annotation instanceof Authorize) { + definition.putAnnotation(((Authorize) annotation)); + } + if (annotation instanceof Resource) { + definition.putAnnotation(((Resource) annotation)); + } + } + } + + private void initMethodAnnotation() { + for (Annotation annotation : methodAnnotation) { + if (annotation instanceof Authorize) { + definition.putAnnotation(((Authorize) annotation)); + } + if (annotation instanceof Resource) { + definition.putAnnotation(((Resource) annotation)); + } + if (annotation instanceof Dimension) { + definition.putAnnotation(((Dimension) annotation)); + } + } + } + + private void initClassDataAccessAnnotation(){ + for (Annotation annotation : classAnnotation) { + if (annotation instanceof DataAccessType || + annotation instanceof DataAccess) { + for (ResourceDefinition resource : definition.getResources().getResources()) { + for (ResourceActionDefinition action : resource.getActions()) { + if (annotation instanceof DataAccessType) { + definition.putAnnotation(action, (DataAccessType) annotation); + } else { + definition.putAnnotation(action, (DataAccess) annotation); + } + } + } + } + } + } + + private void initMethodDataAccessAnnotation() { + for (Annotation annotation : methodAnnotation) { + + if (annotation instanceof ResourceAction) { + getAnnotationByType(Resource.class) + .map(res -> definition.getResources().getResource(res.id()).orElse(null)) + .filter(Objects::nonNull) + .forEach(res -> { + ResourceAction ra = (ResourceAction) annotation; + ResourceActionDefinition action = definition.putAnnotation(res, ra); + getAnnotationByType(DataAccessType.class) + .findFirst() + .ifPresent(dat -> definition.putAnnotation(action, dat)); + }); + } + Optional actionDefinition = getAnnotationByType(Resource.class) + .map(res -> definition.getResources().getResource(res.id()).orElse(null)) + .filter(Objects::nonNull) + .flatMap(res -> getAnnotationByType(ResourceAction.class) + .map(ra -> res.getAction(ra.id()) + .orElse(null)) + ) + .filter(Objects::nonNull) + .findFirst(); + + if (annotation instanceof DataAccessType) { + actionDefinition.ifPresent(ra -> definition.putAnnotation(ra, (DataAccessType) annotation)); + } + + if (annotation instanceof DataAccess) { + actionDefinition.ifPresent(ra -> { + definition.putAnnotation(ra, (DataAccess) annotation); + getAnnotationByType(DataAccessType.class) + .findFirst() + .ifPresent(dat -> definition.putAnnotation(ra, dat)); + }); + } + + } + } + + AopAuthorizeDefinition parse() { + initClassAnnotation(); + initClassDataAccessAnnotation(); + initMethodAnnotation(); + initMethodDataAccessAnnotation(); + + return definition; + } + + + private Stream getAnnotationByType(Class type) { + return Optional.ofNullable(methodAnnotationGroup.getOrDefault(type, classAnnotationGroup.get(type))) + .map(Collection::stream) + .orElseGet(Stream::empty) + .map(type::cast); + } + +} 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 3564361de..d667a4d0d 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 @@ -5,12 +5,14 @@ import lombok.*; import org.hswebframework.web.authorization.annotation.*; import org.hswebframework.web.authorization.define.*; import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.util.StringUtils; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; +import java.util.stream.Stream; /** * 默认权限权限定义 @@ -54,99 +56,7 @@ public class DefaultBasicAuthorizeDefinition implements AopAuthorizeDefinition { )); public static AopAuthorizeDefinition from(Class targetClass, Method method) { - DefaultBasicAuthorizeDefinition definition = new DefaultBasicAuthorizeDefinition(); - definition.setTargetClass(targetClass); - definition.setTargetMethod(method); - - Set methodAnnotation = AnnotatedElementUtils.findAllMergedAnnotations(method, types); - - Set classAnnotation = AnnotatedElementUtils.findAllMergedAnnotations(targetClass, types); - - Map classAnnotationMap = classAnnotation - .stream() - .collect(Collectors.toMap(Annotation::annotationType, Function.identity())); - - Map mapping = methodAnnotation - .stream() - .collect(Collectors.toMap(Annotation::annotationType, Function.identity())); - - for (Annotation annotation : classAnnotation) { - if (annotation instanceof Authorize) { - definition.putAnnotation(((Authorize) annotation)); - } - if (annotation instanceof Resource) { - definition.putAnnotation(((Resource) annotation)); - } - } - - for (Annotation annotation : methodAnnotation) { - if (annotation instanceof Authorize) { - definition.putAnnotation(((Authorize) annotation)); - } - if (annotation instanceof Resource) { - definition.putAnnotation(((Resource) annotation)); - } - if (annotation instanceof Dimension) { - definition.putAnnotation(((Dimension) annotation)); - } - } - - for (Annotation annotation : methodAnnotation) { - - if (annotation instanceof ResourceAction) { - Optional.ofNullable(mapping.getOrDefault(Resource.class, classAnnotationMap.get(Resource.class))) - .map(Resource.class::cast) - .flatMap(res -> definition.getResources().getResource(res.id())) - .ifPresent(res -> { - - ResourceAction ra = (ResourceAction) annotation; - ResourceActionDefinition action = definition.putAnnotation(res, ra); - - Optional.ofNullable(mapping.get(DataAccessType.class)) - .map(DataAccessType.class::cast) - .ifPresent(dat -> definition.putAnnotation(action, dat)); - }); - } - Optional actionDefinition = Optional.ofNullable(mapping.getOrDefault(Resource.class, classAnnotationMap.get(Resource.class))) - .map(Resource.class::cast) - .flatMap(res -> definition.getResources().getResource(res.id())) - .flatMap(res -> Optional.ofNullable(mapping.get(ResourceAction.class)) - .map(ResourceAction.class::cast) - .flatMap(ra -> res.getAction(ra.id()))); - - if (annotation instanceof DataAccessType) { - actionDefinition.ifPresent(ra -> definition.putAnnotation(ra, (DataAccessType) annotation)); - } - - if (annotation instanceof DataAccess) { - actionDefinition.ifPresent(ra -> { - definition.putAnnotation(ra, (DataAccess) annotation); - Optional.ofNullable(mapping.get(DataAccessType.class)) - .map(DataAccessType.class::cast) - .ifPresent(dat -> definition.putAnnotation(ra, dat)); - }); - } - - } - - - for (Annotation annotation : classAnnotation) { - if (annotation instanceof DataAccessType|| - annotation instanceof DataAccess) { - for (ResourceDefinition resource : definition.getResources().getResources()) { - for (ResourceActionDefinition action : resource.getActions()) { - if(annotation instanceof DataAccessType) { - definition.putAnnotation(action, (DataAccessType) annotation); - }else{ - definition.putAnnotation(action, (DataAccess) annotation); - } - } - } - } - } - - - return definition; + return new AopAuthorizeDefinitionParser(targetClass,method).parse(); } public void putAnnotation(Authorize ann) { @@ -208,18 +118,27 @@ public class DefaultBasicAuthorizeDefinition implements AopAuthorizeDefinition { } DataAccessTypeDefinition typeDefinition = new DataAccessTypeDefinition(); for (DataAccessType dataAccessType : ann.type()) { + if(dataAccessType.ignore()){ + continue; + } typeDefinition.setId(dataAccessType.id()); typeDefinition.setName(dataAccessType.name()); typeDefinition.setController(dataAccessType.controller()); typeDefinition.setConfiguration(dataAccessType.configuration()); typeDefinition.setDescription(String.join("\n", dataAccessType.description())); } + if(StringUtils.isEmpty(typeDefinition.getId())){ + return; + } definition.getDataAccess() .getDataAccessTypes() .add(typeDefinition); } public void putAnnotation(ResourceActionDefinition definition, DataAccessType dataAccessType) { + if(dataAccessType.ignore()){ + return; + } DataAccessTypeDefinition typeDefinition = new DataAccessTypeDefinition(); typeDefinition.setId(dataAccessType.id()); typeDefinition.setName(dataAccessType.name()); diff --git a/hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/basic/aop/AopAuthorizingControllerTest.java b/hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/basic/aop/AopAuthorizingControllerTest.java index a75ac701b..31ed54700 100644 --- a/hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/basic/aop/AopAuthorizingControllerTest.java +++ b/hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/basic/aop/AopAuthorizingControllerTest.java @@ -10,6 +10,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; @@ -32,7 +33,7 @@ public class AopAuthorizingControllerTest { authentication.setUser(SimpleUser.builder().id("test").username("test").build()); // authentication.setPermissions(Arrays.asList(SimplePermission.builder().id("test").build())); authentication.setPermissions(Collections.emptyList()); - ReactiveAuthenticationHolder.addSupplier(new ReactiveAuthenticationSupplier() { + ReactiveAuthenticationHolder.setSupplier(new ReactiveAuthenticationSupplier() { @Override public Mono get(String userId) { return Mono.empty(); @@ -73,7 +74,7 @@ public class AopAuthorizingControllerTest { .dataAccesses(Collections.singleton(config)) .id("test").build())); - ReactiveAuthenticationHolder.addSupplier(new ReactiveAuthenticationSupplier() { + ReactiveAuthenticationHolder.setSupplier(new ReactiveAuthenticationSupplier() { @Override public Mono get(String userId) { return Mono.empty(); @@ -109,7 +110,7 @@ public class AopAuthorizingControllerTest { DimensionDataAccessConfig config2 = new DimensionDataAccessConfig(); config2.setAction("save"); config2.setScopeType("role"); - ReactiveAuthenticationHolder.addSupplier(new ReactiveAuthenticationSupplier() { + ReactiveAuthenticationHolder.setSupplier(new ReactiveAuthenticationSupplier() { @Override public Mono get(String userId) { return Mono.empty(); diff --git a/hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/basic/aop/TestController.java b/hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/basic/aop/TestController.java index 880eaed5f..601790a8b 100644 --- a/hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/basic/aop/TestController.java +++ b/hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/basic/aop/TestController.java @@ -33,6 +33,7 @@ public class TestController implements ReactiveCrudController queryUser(QueryParam queryParam) { return Mono.just(queryParam); } diff --git a/hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/basic/define/DefaultBasicAuthorizeDefinitionTest.java b/hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/basic/define/DefaultBasicAuthorizeDefinitionTest.java index 24953d3a6..54d6bf245 100644 --- a/hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/basic/define/DefaultBasicAuthorizeDefinitionTest.java +++ b/hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/basic/define/DefaultBasicAuthorizeDefinitionTest.java @@ -26,7 +26,7 @@ public class DefaultBasicAuthorizeDefinitionTest { Assert.assertTrue(resource.hasAction(Arrays.asList("add"))); Assert.assertTrue(resource.getAction("add") - .map(act->act.getDataAccess().getType("user_own")) + .map(act->act.getDataAccess().getType("user_own_data")) .isPresent()); }