Browse Source

优化权限解析

zhouhao 5 years ago
parent
commit
9af9811f57
12 changed files with 206 additions and 104 deletions
  1. 9 0
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/Authentication.java
  2. 11 1
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/ReactiveAuthenticationHolder.java
  3. 1 0
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/annotation/DataAccessType.java
  4. 4 1
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/annotation/DimensionDataAccess.java
  5. 5 1
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/annotation/FieldDataAccess.java
  6. 0 1
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/annotation/UserOwnData.java
  7. 3 3
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/define/DataAccessDefinition.java
  8. 155 0
      hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/define/AopAuthorizeDefinitionParser.java
  9. 12 93
      hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/define/DefaultBasicAuthorizeDefinition.java
  10. 4 3
      hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/basic/aop/AopAuthorizingControllerTest.java
  11. 1 0
      hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/basic/aop/TestController.java
  12. 1 1
      hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/basic/define/DefaultBasicAuthorizeDefinitionTest.java

+ 9 - 0
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/Authentication.java

@@ -30,6 +30,7 @@ import java.util.stream.Collectors;
  * <ul>
  * <li>springmvc 入参方式: ResponseMessage myTest(Authorization auth){}</li>
  * <li>静态方法方式:AuthorizationHolder.get();</li>
+ * <li>响应式方式: return Authentication.currentReactive().map(auth->....)</li>
  * </ul>
  *
  * @author zhouhao
@@ -51,6 +52,7 @@ public interface Authentication extends Serializable {
      *
      * @return 当前用户权限信息
      * @see ReactiveAuthenticationHolder
+     * @since 4.0
      */
     static Mono<Authentication> currentReactive() {
         return ReactiveAuthenticationHolder.get();
@@ -81,6 +83,7 @@ public interface Authentication extends Serializable {
 
     /**
      * @return 用户所有维度
+     * @since 4.0
      */
     List<Dimension> getDimensions();
 
@@ -196,6 +199,12 @@ public interface Authentication extends Serializable {
      */
     Map<String, Serializable> getAttributes();
 
+    /**
+     * 合并权限
+     *
+     * @param source 源权限信息
+     * @return 合并后的信息
+     */
     Authentication merge(Authentication source);
 
 }

+ 11 - 1
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<ReactiveAuthenticationSupplier> 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();
+        }
+    }
+
 }

+ 1 - 0
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/annotation/DataAccessType.java

@@ -24,4 +24,5 @@ public @interface DataAccessType {
 
     Class<? extends DataAccessConfiguration> configuration() default DataAccessConfiguration.class;
 
+    boolean ignore() default false;
 }

+ 4 - 1
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

+ 5 - 1
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;
 }

+ 0 - 1
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 {
 

+ 3 - 3
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<DataAccessTypeDefinition> dataAccessTypes=new HashSet<>();
+    Set<DataAccessTypeDefinition> dataAccessTypes = new HashSet<>();
 
-    public Optional<DataAccessTypeDefinition> getType(String typeId){
+    public Optional<DataAccessTypeDefinition> getType(String typeId) {
         return dataAccessTypes
                 .stream()
-                .filter(type->type.getId().equalsIgnoreCase(typeId))
+                .filter(type -> type.getId() != null && type.getId().equalsIgnoreCase(typeId))
                 .findAny();
     }
 }

+ 155 - 0
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<Class<? extends Annotation>> types = new HashSet<>(Arrays.asList(
+            Authorize.class,
+            DataAccess.class,
+            Dimension.class,
+            Resource.class,
+            ResourceAction.class,
+            DataAccessType.class
+    ));
+
+    private Set<Annotation> methodAnnotation;
+
+    private Set<Annotation> classAnnotation;
+
+    private Map<Class<? extends Annotation>, List<Annotation>> classAnnotationGroup;
+
+    private Map<Class<? extends Annotation>, List<Annotation>> 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<ResourceActionDefinition> 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 <T extends Annotation> Stream<T> getAnnotationByType(Class<T> type) {
+        return Optional.ofNullable(methodAnnotationGroup.getOrDefault(type, classAnnotationGroup.get(type)))
+                .map(Collection::stream)
+                .orElseGet(Stream::empty)
+                .map(type::cast);
+    }
+
+}

+ 12 - 93
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<Annotation> methodAnnotation = AnnotatedElementUtils.findAllMergedAnnotations(method, types);
-
-        Set<Annotation> classAnnotation = AnnotatedElementUtils.findAllMergedAnnotations(targetClass, types);
-
-        Map<Class, Annotation> classAnnotationMap = classAnnotation
-                .stream()
-                .collect(Collectors.toMap(Annotation::annotationType, Function.identity()));
-
-        Map<Class, Annotation> 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<ResourceActionDefinition> 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());

+ 4 - 3
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<Authentication> 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<Authentication> 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<Authentication> get(String userId) {
                 return Mono.empty();

+ 1 - 0
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<TestEntity, String
 
     @QueryAction
     @FieldDataAccess
+    @DimensionDataAccess(ignore = true)
     public Mono<QueryParam> queryUser(QueryParam queryParam) {
         return Mono.just(queryParam);
     }

+ 1 - 1
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());
     }