Browse Source

优化权限

zhou-hao 5 years ago
parent
commit
9d206c0580
67 changed files with 1361 additions and 348 deletions
  1. 2 0
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/Authentication.java
  2. 2 2
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/AuthenticationHolder.java
  3. 0 85
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/CompositeAuthentication.java
  4. 18 0
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/Dimension.java
  5. 11 0
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/DimensionProvider.java
  6. 0 49
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/MultiAuthentication.java
  7. 1 3
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/Permission.java
  8. 3 3
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/ReactiveAuthenticationHolder.java
  9. 21 0
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/ReactiveAuthenticationManagerProvider.java
  10. 21 0
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/annotation/RequiresRoles.java
  11. 2 0
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/annotation/Resource.java
  12. 2 0
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/define/ResourceDefinition.java
  13. 7 1
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/define/ResourcesDefinition.java
  14. 46 0
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/CompositeReactiveAuthenticationManager.java
  15. 10 2
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/DefaultAuthorizationAutoConfiguration.java
  16. 33 31
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/SimpleAuthentication.java
  17. 4 6
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/SimpleDimension.java
  18. 10 0
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/SimplePermission.java
  19. 3 2
      hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/configuration/AuthorizingHandlerAutoConfiguration.java
  20. 1 0
      hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/define/DefaultBasicAuthorizeDefinition.java
  21. 2 1
      hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/embed/EmbedReactiveAuthenticationManager.java
  22. 11 9
      hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/AuthorizationController.java
  23. 56 0
      hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/basic/web/CompositeReactiveAuthenticationManagerTest.java
  24. 5 1
      hsweb-commons/hsweb-commons-api/src/main/java/org/hswebframework/web/api/crud/entity/EntityFactory.java
  25. 0 2
      hsweb-commons/hsweb-commons-api/src/main/java/org/hswebframework/web/api/crud/entity/SortSupportEntity.java
  26. 21 13
      hsweb-commons/hsweb-commons-api/src/main/java/org/hswebframework/web/api/crud/entity/TreeSupportEntity.java
  27. 0 1
      hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/configuration/AutoDDLProcessor.java
  28. 1 1
      hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/configuration/EasyOrmConfiguration.java
  29. 23 14
      hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/entity/factory/MapperEntityFactory.java
  30. 3 1
      hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/generator/DefaultIdGenerator.java
  31. 2 0
      hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/generator/MD5Generator.java
  32. 10 5
      hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/service/ReactiveCrudService.java
  33. 76 0
      hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/service/ReactiveTreeSortEntityService.java
  34. 8 8
      hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/CommonErrorControllerAdvice.java
  35. 2 0
      hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/CommonWebFluxConfiguration.java
  36. 4 0
      hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/reactive/ReactiveDeleteController.java
  37. 9 7
      hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/reactive/ReactiveQueryController.java
  38. 8 3
      hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/reactive/ReactiveSaveController.java
  39. 9 0
      hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/reactive/ReactiveServiceCrudController.java
  40. 25 0
      hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/reactive/ReactiveServiceDeleteController.java
  41. 83 0
      hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/reactive/ReactiveServiceQueryController.java
  42. 72 0
      hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/reactive/ReactiveServiceSaveController.java
  43. 25 0
      hsweb-commons/hsweb-commons-crud/src/test/java/org/hswebframework/web/crud/entity/TestTreeSortEntity.java
  44. 62 0
      hsweb-commons/hsweb-commons-crud/src/test/java/org/hswebframework/web/crud/service/ReactiveTreeSortEntityServiceTest.java
  45. 33 0
      hsweb-commons/hsweb-commons-crud/src/test/java/org/hswebframework/web/crud/service/TestTreeSortEntityService.java
  46. 4 10
      hsweb-datasource/hsweb-datasource-web/src/main/java/org/hswebframework/web/datasource/web/DatasourceController.java
  47. 4 0
      hsweb-starter/pom.xml
  48. 37 0
      hsweb-starter/src/main/java/org/hswebframework/web/starter/jackson/CustomCodecsAutoConfiguration.java
  49. 155 0
      hsweb-starter/src/main/java/org/hswebframework/web/starter/jackson/CustomJackson2JsonDecoder.java
  50. 204 0
      hsweb-starter/src/main/java/org/hswebframework/web/starter/jackson/Jackson2Tokenizer.java
  51. 3 0
      hsweb-starter/src/main/resources/META-INF/spring.factories
  52. 0 26
      hsweb-system/hsweb-system-authorization/hsweb-system-authorization-api/src/main/java/org/hswebframework/web/system/authorization/api/PermissionDimension.java
  53. 0 11
      hsweb-system/hsweb-system-authorization/hsweb-system-authorization-api/src/main/java/org/hswebframework/web/system/authorization/api/PermissionDimensionProvider.java
  54. 5 3
      hsweb-system/hsweb-system-authorization/hsweb-system-authorization-api/src/main/java/org/hswebframework/web/system/authorization/api/UserPermissionDimensionProvider.java
  55. 11 1
      hsweb-system/hsweb-system-authorization/hsweb-system-authorization-api/src/main/java/org/hswebframework/web/system/authorization/api/entity/AuthorizationSettingEntity.java
  56. 1 1
      hsweb-system/hsweb-system-authorization/hsweb-system-authorization-api/src/main/java/org/hswebframework/web/system/authorization/api/entity/PermissionEntity.java
  57. 8 9
      hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/java/org/hswebframework/web/system/authorization/defaults/configuration/AuthorizationServiceAutoConfiguration.java
  58. 1 1
      hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/java/org/hswebframework/web/system/authorization/defaults/configuration/AuthorizationWebAutoConfiguration.java
  59. 32 0
      hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/java/org/hswebframework/web/system/authorization/defaults/service/DefaultDimensionService.java
  60. 9 6
      hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/java/org/hswebframework/web/system/authorization/defaults/service/DefaultReactiveAuthenticationInitializeService.java
  61. 2 5
      hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/java/org/hswebframework/web/system/authorization/defaults/service/DefaultReactiveAuthenticationManager.java
  62. 7 8
      hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/java/org/hswebframework/web/system/authorization/defaults/service/DefaultReactiveUserService.java
  63. 26 0
      hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/java/org/hswebframework/web/system/authorization/defaults/webflux/WebFluxAuthorizationSettingController.java
  64. 37 0
      hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/java/org/hswebframework/web/system/authorization/defaults/webflux/WebFluxDimensionController.java
  65. 9 6
      hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/java/org/hswebframework/web/system/authorization/defaults/webflux/WebFluxPermissionController.java
  66. 59 3
      hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/java/org/hswebframework/web/system/authorization/defaults/webflux/WebFluxUserController.java
  67. 0 8
      hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/test/java/org/hswebframework/web/system/authorization/defaults/service/reactive/ReactiveTestApplication.java

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

@@ -183,4 +183,6 @@ public interface Authentication extends Serializable {
      */
     Map<String, Serializable> getAttributes();
 
+    Authentication merge(Authentication source);
+
 }

+ 2 - 2
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/AuthenticationHolder.java

@@ -18,6 +18,7 @@
 
 package org.hswebframework.web.authorization;
 
+import org.hswebframework.web.authorization.simple.SimpleAuthentication;
 import reactor.core.publisher.Flux;
 import reactor.core.publisher.Mono;
 
@@ -54,8 +55,7 @@ public final class AuthenticationHolder {
         return Flux.fromStream(suppliers.stream().map(function))
                 .filter(Optional::isPresent)
                 .map(Optional::get)
-                .reduceWith(CompositeAuthentication::new, CompositeAuthentication::merge)
-                .filter(CompositeAuthentication::isNotEmpty)
+                .reduceWith(SimpleAuthentication::new, SimpleAuthentication::merge)
                 .map(Authentication.class::cast)
                 .blockOptional();
     }

+ 0 - 85
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/CompositeAuthentication.java

@@ -1,85 +0,0 @@
-package org.hswebframework.web.authorization;
-
-import java.io.Serializable;
-import java.util.*;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-
-public class CompositeAuthentication implements Authentication {
-
-    private Map<String, Authentication> userAuthentication = new ConcurrentHashMap<>();
-
-    private String currentUser;
-
-    public boolean isEmpty() {
-        return userAuthentication.isEmpty();
-    }
-
-    public boolean isNotEmpty() {
-        return !isEmpty();
-    }
-
-    @Override
-    public User getUser() {
-
-        return userAuthentication
-                .get(currentUser)
-                .getUser();
-    }
-
-    @Override
-    public List<Dimension> getDimensions() {
-        return userAuthentication.values()
-                .stream()
-                .map(Authentication::getDimensions)
-                .flatMap(Collection::stream)
-                .collect(Collectors.toList());
-    }
-
-    @Override
-    public List<Role> getRoles() {
-        return userAuthentication.values()
-                .stream()
-                .map(Authentication::getRoles)
-                .flatMap(Collection::stream)
-                .collect(Collectors.toList());
-    }
-
-    @Override
-    public List<Permission> getPermissions() {
-        return userAuthentication.values()
-                .stream()
-                .map(Authentication::getPermissions)
-                .flatMap(Collection::stream)
-                .collect(Collectors.toList());
-    }
-
-    @Override
-    public <T extends Serializable> Optional<T> getAttribute(String name) {
-        return userAuthentication.values()
-                .stream()
-                .map(a -> a.<T>getAttribute(name))
-                .filter(Optional::isPresent)
-                .findAny()
-                .flatMap(Function.identity());
-    }
-
-    @Override
-    public Map<String, Serializable> getAttributes() {
-        return userAuthentication.values()
-                .stream()
-                .map(Authentication::getAttributes)
-                .filter(Objects::nonNull)
-                .reduce(new HashMap<>(),(r,s)->{r.putAll(s);return r;});
-    }
-
-    public CompositeAuthentication merge(Authentication authentication) {
-        String userId = authentication.getUser().getId();
-        if (currentUser == null) {
-            currentUser = userId;
-        }
-        userAuthentication.put(userId, authentication);
-        return this;
-    }
-}

+ 18 - 0
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/Dimension.java

@@ -1,5 +1,7 @@
 package org.hswebframework.web.authorization;
 
+import org.hswebframework.web.authorization.simple.SimpleDimension;
+
 import java.io.Serializable;
 import java.util.Map;
 import java.util.Optional;
@@ -18,4 +20,20 @@ public interface Dimension extends Serializable {
                 .map(ops -> ops.get(key))
                 .map(o -> (T) o);
     }
+
+    default boolean typeIs(DimensionType type) {
+        return this.getType() == type || this.getType().getId().equals(type.getId());
+    }
+
+    default boolean typeIs(String type) {
+        return this.getType().getId().equals(type);
+    }
+
+    static Dimension of(String id, String name, DimensionType type) {
+        return of(id, name, type, null);
+    }
+
+    static Dimension of(String id, String name, DimensionType type, Map<String, Object> options) {
+        return SimpleDimension.of(id, name, type, options);
+    }
 }

+ 11 - 0
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/DimensionProvider.java

@@ -0,0 +1,11 @@
+package org.hswebframework.web.authorization;
+
+import reactor.core.publisher.Flux;
+
+public interface DimensionProvider {
+
+    Flux<Dimension> getDimensionByUserId(String userId);
+
+    Flux<String> getUserIdByDimensionId(String dimensionId);
+
+}

+ 0 - 49
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/MultiAuthentication.java

@@ -1,49 +0,0 @@
-/*
- *  Copyright 2019 http://www.hswebframework.org
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- *
- *
- */
-
-package org.hswebframework.web.authorization;
-
-import java.util.Set;
-
-/**
- * 多用户权限,可同时登录多个用户,调用{@link Authentication}的方法为获取当前激活用户的权限
- *
- * @since 3.0
- */
-public interface MultiAuthentication extends Authentication {
-
-    /**
-     * @return 所有权限信息
-     */
-    Set<Authentication> getAuthentications();
-
-    /**
-     * 激活指定的用户
-     *
-     * @param userId 用户ID
-     * @return 被激活的用户, 如果用户未登录, 则返回null
-     */
-    Authentication activate(String userId);
-
-    /**
-     * 添加一个授权
-     *
-     * @param authentication 授权信息
-     */
-    void addAuthentication(Authentication authentication);
-}

+ 1 - 3
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/Permission.java

@@ -17,7 +17,6 @@
 
 package org.hswebframework.web.authorization;
 
-import lombok.NonNull;
 import org.hswebframework.web.authorization.access.DataAccessConfig;
 import org.hswebframework.web.authorization.access.FieldFilterDataAccessConfig;
 import org.hswebframework.web.authorization.access.ScopeDataAccessConfig;
@@ -146,7 +145,6 @@ public interface Permission extends Serializable {
         return findDataAccess(conf -> FieldFilterDataAccessConfig.class.isInstance(conf) && conf.getAction().equals(action));
     }
 
-
     /**
      * 获取不能执行操作的字段
      *
@@ -199,7 +197,7 @@ public interface Permission extends Serializable {
                         && scopeType.equals(((ScopeDataAccessConfig) config).getScopeType());
     }
 
-
+    Permission copy();
     /**
      * 数据权限查找判断逻辑接口
      *

+ 3 - 3
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/ReactiveAuthenticationHolder.java

@@ -18,6 +18,7 @@
 
 package org.hswebframework.web.authorization;
 
+import org.hswebframework.web.authorization.simple.SimpleAuthentication;
 import reactor.core.publisher.Flux;
 import reactor.core.publisher.Mono;
 
@@ -53,9 +54,8 @@ public final class ReactiveAuthenticationHolder {
         return Flux.concat(suppliers.stream()
                 .map(function)
                 .collect(Collectors.toList()))
-                .reduceWith(CompositeAuthentication::new, CompositeAuthentication::merge)
-                .filter(CompositeAuthentication::isNotEmpty)
-                .map(Authentication.class::cast);
+                .reduceWith(SimpleAuthentication::new, Authentication::merge)
+                .filter(a -> a.getUser() != null);
     }
 
     /**

+ 21 - 0
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/ReactiveAuthenticationManagerProvider.java

@@ -0,0 +1,21 @@
+package org.hswebframework.web.authorization;
+
+import reactor.core.publisher.Mono;
+
+public interface ReactiveAuthenticationManagerProvider {
+    /**
+     * 进行授权操作
+     *
+     * @param request 授权请求
+     * @return 授权成功则返回用户权限信息
+     */
+    Mono<Authentication> authenticate(Mono<AuthenticationRequest> request);
+
+    /**
+     * 根据用户ID获取权限信息
+     *
+     * @param userId 用户ID
+     * @return 权限信息
+     */
+    Mono<Authentication> getByUserId(String userId);
+}

+ 21 - 0
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/annotation/RequiresRoles.java

@@ -0,0 +1,21 @@
+package org.hswebframework.web.authorization.annotation;
+
+
+import org.springframework.core.annotation.AliasFor;
+
+import java.lang.annotation.*;
+
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+@Documented
+@Dimension(type = "role", description = "控制角色")
+public @interface RequiresRoles {
+
+    @AliasFor(annotation = Dimension.class, attribute = "id")
+    String[] value() default {};
+
+    @AliasFor(annotation = Dimension.class, attribute = "logical")
+    Logical logical() default Logical.DEFAULT;
+
+}

+ 2 - 0
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/annotation/Resource.java

@@ -22,5 +22,7 @@ public @interface Resource {
 
     String[] description() default {};
 
+    String[] group() default {};
+
     boolean merge() default true;
 }

+ 2 - 0
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/define/ResourceDefinition.java

@@ -20,6 +20,8 @@ public class ResourceDefinition {
 
     private List<ResourceActionDefinition> actions=new ArrayList<>();
 
+    private List<String> group;
+
     @Setter(value = AccessLevel.PRIVATE)
     private volatile Set<String> actionIds;
 

+ 7 - 1
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/define/ResourcesDefinition.java

@@ -20,7 +20,6 @@ public class ResourcesDefinition {
 
     private Phased phased = Phased.before;
 
-
     public void addResource(ResourceDefinition resource, boolean merge) {
         ResourceDefinition definition = getResource(resource.getId()).orElse(null);
         if (definition != null) {
@@ -49,12 +48,19 @@ public class ResourcesDefinition {
     }
 
     public boolean hasPermission(Permission permission) {
+        if (CollectionUtils.isEmpty(resources)) {
+            return true;
+        }
         return getResource(permission.getId())
                 .filter(resource -> resource.hasAction(permission.getActions()))
                 .isPresent();
     }
 
     public boolean hasPermission(Collection<Permission> permissions) {
+
+        if (CollectionUtils.isEmpty(resources)) {
+            return true;
+        }
         if (CollectionUtils.isEmpty(permissions)) {
             return false;
         }

+ 46 - 0
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/CompositeReactiveAuthenticationManager.java

@@ -0,0 +1,46 @@
+package org.hswebframework.web.authorization.simple;
+
+import lombok.AllArgsConstructor;
+import org.hswebframework.web.authorization.*;
+import org.reactivestreams.Publisher;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+import java.util.List;
+import java.util.function.Function;
+
+@AllArgsConstructor
+public class CompositeReactiveAuthenticationManager implements ReactiveAuthenticationManager {
+
+    private List<ReactiveAuthenticationManagerProvider> providers;
+
+    @Override
+    public Mono<Authentication> authenticate(Mono<AuthenticationRequest> request) {
+        return Flux
+                .fromStream(providers.stream()
+                        .map(manager -> manager
+                                .authenticate(request)
+                                .onErrorResume((err) -> {
+                                    return Mono.empty();
+                                })
+                        ))
+                .flatMap(Function.identity())
+                .reduceWith(SimpleAuthentication::of, Authentication::merge)
+                .filter(a -> a.getUser() != null);
+    }
+
+    @Override
+    public Mono<Authentication> getByUserId(String userId) {
+        return Flux
+                .fromStream(providers.stream()
+                        .map(manager -> manager
+                                .getByUserId(userId)
+                                .onErrorResume((err) -> {
+                                    return Mono.empty();
+                                })
+                        ))
+                .flatMap(Function.identity())
+                .reduceWith(SimpleAuthentication::of, Authentication::merge)
+                .filter(a -> a.getUser() != null);
+    }
+}

+ 10 - 2
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/DefaultAuthorizationAutoConfiguration.java

@@ -18,6 +18,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 import org.springframework.boot.context.properties.ConfigurationProperties;
 import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Conditional;
 import org.springframework.context.annotation.Configuration;
 
 import java.util.List;
@@ -33,15 +34,22 @@ public class DefaultAuthorizationAutoConfiguration {
 
     @Bean
     @ConditionalOnMissingBean(UserTokenManager.class)
-    @ConfigurationProperties(prefix = "hsweb.authorize")
+    @ConfigurationProperties(prefix = "hsweb.user-token")
     public UserTokenManager userTokenManager() {
         return new DefaultUserTokenManager();
     }
 
+    @Bean
+    @ConditionalOnMissingBean
+    @ConditionalOnBean(ReactiveAuthenticationManagerProvider.class)
+    public ReactiveAuthenticationManager reactiveAuthenticationManager(List<ReactiveAuthenticationManagerProvider> providers) {
+        return new CompositeReactiveAuthenticationManager(providers);
+    }
+
     @Bean
     @ConditionalOnBean(ReactiveAuthenticationManager.class)
     public UserTokenReactiveAuthenticationSupplier userTokenReactiveAuthenticationSupplier(UserTokenManager userTokenManager,
-                                                                                   ReactiveAuthenticationManager authenticationManager) {
+                                                                                           ReactiveAuthenticationManager authenticationManager) {
         UserTokenReactiveAuthenticationSupplier supplier = new UserTokenReactiveAuthenticationSupplier(userTokenManager, authenticationManager);
         ReactiveAuthenticationHolder.addSupplier(supplier);
         return supplier;

+ 33 - 31
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/SimpleAuthentication.java

@@ -18,53 +18,31 @@
 package org.hswebframework.web.authorization.simple;
 
 import lombok.Getter;
+import lombok.Setter;
 import org.hswebframework.web.authorization.*;
 
 import java.io.Serializable;
 import java.util.*;
+import java.util.function.Function;
+import java.util.stream.Collectors;
 
+@Getter
+@Setter
 public class SimpleAuthentication implements Authentication {
 
     private static final long serialVersionUID = -2898863220255336528L;
 
     private User user;
 
-    private List<Permission> permissions;
+    private List<Permission> permissions = new ArrayList<>();
 
-    private List<Dimension> dimensions;
+    private List<Dimension> dimensions = new ArrayList<>();
 
-    @Getter
     private Map<String, Serializable> attributes = new HashMap<>();
 
-    @Override
-    public User getUser() {
-        return user;
-    }
-
-    public void setUser(User user) {
-        this.user = user;
-    }
-
-    public void setPermissions(List<Permission> permissions) {
-        this.permissions = permissions;
-    }
-
-    @Override
-    public List<Permission> getPermissions() {
-        if (permissions == null) {
-            return permissions = new ArrayList<>();
-        }
-        return new ArrayList<>(permissions);
-    }
-
-    @Override
-    public List<Dimension> getDimensions() {
-        if (dimensions == null) {
-            return dimensions = new ArrayList<>();
-        }
-        return dimensions;
+    public static Authentication of(){
+        return new SimpleAuthentication();
     }
-
     @Override
     @SuppressWarnings("unchecked")
     public <T extends Serializable> Optional<T> getAttribute(String name) {
@@ -75,4 +53,28 @@ public class SimpleAuthentication implements Authentication {
     public Map<String, Serializable> getAttributes() {
         return attributes;
     }
+
+    public SimpleAuthentication merge(Authentication authentication) {
+        Map<String, Permission> mePermissionGroup = permissions.stream()
+                .collect(Collectors.toMap(Permission::getId, Function.identity()));
+        user = authentication.getUser();
+        attributes.putAll(authentication.getAttributes());
+        for (Permission permission : authentication.getPermissions()) {
+            Permission me = mePermissionGroup.get(permission.getId());
+            if (me == null) {
+                permissions.add(permission.copy());
+                continue;
+            }
+            me.getActions().addAll(permission.getActions());
+            me.getDataAccesses().addAll(permission.getDataAccesses());
+        }
+
+
+        for (Dimension dimension : authentication.getDimensions()) {
+            if (!getDimension(dimension.getType(), dimension.getId()).isPresent()) {
+                dimensions.add(dimension);
+            }
+        }
+        return this;
+    }
 }

+ 4 - 6
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/SimpleDimension.java

@@ -1,6 +1,8 @@
 package org.hswebframework.web.authorization.simple;
 
+import lombok.AllArgsConstructor;
 import lombok.Getter;
+import lombok.NoArgsConstructor;
 import lombok.Setter;
 import org.hswebframework.web.authorization.Dimension;
 import org.hswebframework.web.authorization.DimensionType;
@@ -9,6 +11,8 @@ import java.util.Map;
 
 @Getter
 @Setter
+@AllArgsConstructor(staticName = "of")
+@NoArgsConstructor
 public class SimpleDimension implements Dimension {
 
     private String id;
@@ -19,11 +23,5 @@ public class SimpleDimension implements Dimension {
 
     private Map<String,Object> options;
 
-    public boolean typeIs(DimensionType type) {
-        return this.type == type || this.type.getId().equals(type.getId());
-    }
 
-    public boolean typeIs(String type) {
-        return this.type.getId().equals(type);
-    }
 }

+ 10 - 0
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/SimplePermission.java

@@ -42,4 +42,14 @@ public class SimplePermission implements Permission {
         }
         return dataAccesses;
     }
+
+    public Permission copy(){
+        SimplePermission permission =new SimplePermission();
+
+        permission.setId(id);
+        permission.setName(name);
+        permission.setActions(new HashSet<>(getActions()));
+        permission.setDataAccesses(new HashSet<>(getDataAccesses()));
+        return permission;
+    }
 }

+ 3 - 2
hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/configuration/AuthorizingHandlerAutoConfiguration.java

@@ -2,6 +2,7 @@ package org.hswebframework.web.authorization.basic.configuration;
 
 import org.hswebframework.web.authorization.AuthenticationManager;
 import org.hswebframework.web.authorization.ReactiveAuthenticationManager;
+import org.hswebframework.web.authorization.ReactiveAuthenticationManagerProvider;
 import org.hswebframework.web.authorization.access.DataAccessController;
 import org.hswebframework.web.authorization.access.DataAccessHandler;
 import org.hswebframework.web.authorization.basic.aop.AopMethodAuthorizeDefinitionParser;
@@ -99,8 +100,8 @@ public class AuthorizingHandlerAutoConfiguration {
     }
 
     @Bean
-    @ConditionalOnMissingBean(ReactiveAuthenticationManager.class)
-    public ReactiveAuthenticationManager embedAuthenticationManager(EmbedAuthenticationProperties properties) {
+//    @ConditionalOnMissingBean(ReactiveAuthenticationManager.class)
+    public ReactiveAuthenticationManagerProvider embedAuthenticationManager(EmbedAuthenticationProperties properties) {
         return new EmbedReactiveAuthenticationManager(properties);
     }
 

+ 1 - 0
hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/define/DefaultBasicAuthorizeDefinition.java

@@ -159,6 +159,7 @@ public class DefaultBasicAuthorizeDefinition implements AopAuthorizeDefinition {
         for (ResourceAction action : ann.actions()) {
             putAnnotation(resource, action);
         }
+        resource.setGroup(new ArrayList<>(Arrays.asList(ann.group())));
         resources.addResource(resource, ann.merge());
     }
 

+ 2 - 1
hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/embed/EmbedReactiveAuthenticationManager.java

@@ -4,6 +4,7 @@ import lombok.AllArgsConstructor;
 import org.hswebframework.web.authorization.Authentication;
 import org.hswebframework.web.authorization.AuthenticationRequest;
 import org.hswebframework.web.authorization.ReactiveAuthenticationManager;
+import org.hswebframework.web.authorization.ReactiveAuthenticationManagerProvider;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.core.Ordered;
 import org.springframework.core.annotation.Order;
@@ -16,7 +17,7 @@ import reactor.core.publisher.Mono;
 
 @Order(Ordered.HIGHEST_PRECEDENCE)
 @AllArgsConstructor
-public class EmbedReactiveAuthenticationManager implements ReactiveAuthenticationManager {
+public class EmbedReactiveAuthenticationManager implements ReactiveAuthenticationManagerProvider {
 
     private EmbedAuthenticationProperties properties;
 

+ 11 - 9
hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/AuthorizationController.java

@@ -17,18 +17,19 @@
 
 package org.hswebframework.web.authorization.basic.web;
 
-import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiParam;
 import lombok.SneakyThrows;
 import org.hswebframework.web.authorization.Authentication;
-import org.hswebframework.web.authorization.AuthenticationManager;
 import org.hswebframework.web.authorization.ReactiveAuthenticationManager;
 import org.hswebframework.web.authorization.annotation.Authorize;
-import org.hswebframework.web.authorization.events.*;
+import org.hswebframework.web.authorization.events.AuthorizationBeforeEvent;
+import org.hswebframework.web.authorization.events.AuthorizationDecodeEvent;
+import org.hswebframework.web.authorization.events.AuthorizationFailedEvent;
+import org.hswebframework.web.authorization.events.AuthorizationSuccessEvent;
 import org.hswebframework.web.authorization.exception.UnAuthorizedException;
+import org.hswebframework.web.authorization.simple.CompositeReactiveAuthenticationManager;
 import org.hswebframework.web.authorization.simple.PlainTextUsernamePasswordAuthenticationRequest;
-import org.hswebframework.web.logging.AccessLogger;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.http.MediaType;
@@ -36,7 +37,6 @@ import org.springframework.util.Assert;
 import org.springframework.web.bind.annotation.*;
 import reactor.core.publisher.Mono;
 
-import javax.servlet.http.HttpServletRequest;
 import java.util.Map;
 import java.util.function.Function;
 
@@ -45,16 +45,15 @@ import java.util.function.Function;
  */
 @RestController
 @RequestMapping("${hsweb.web.mappings.authorize:authorize}")
-@AccessLogger("授权")
-@Api(tags = "权限-用户授权", value = "授权")
 public class AuthorizationController {
 
-    @Autowired
-    private ReactiveAuthenticationManager authenticationManager;
 
     @Autowired
     private ApplicationEventPublisher eventPublisher;
 
+    @Autowired
+    private ReactiveAuthenticationManager authenticationManager;
+
     @GetMapping("/me")
     @Authorize
     @ApiOperation("当前登录用户权限信息")
@@ -65,6 +64,7 @@ public class AuthorizationController {
 
     @PostMapping(value = "/login", consumes = MediaType.APPLICATION_JSON_VALUE)
     @ApiOperation("用户名密码登录,json方式")
+    @Authorize(ignore = true)
     public Mono<Map<String, Object>> authorizeByJson(@ApiParam(example = "{\"username\":\"admin\",\"password\":\"admin\"}")
                                                      @RequestBody Mono<Map<String, Object>> parameter) {
         return doLogin(parameter);
@@ -72,6 +72,7 @@ public class AuthorizationController {
 
     @PostMapping(value = "/login", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
     @ApiOperation("用户名密码登录,参数方式")
+    @Authorize(ignore = true)
     public Mono<Map<String, Object>> authorizeByUrlEncoded(@ApiParam(hidden = true) @RequestParam Map<String, Object> parameter) {
 
         return doLogin(Mono.just(parameter));
@@ -102,6 +103,7 @@ public class AuthorizationController {
                 // 验证通过
                 return authenticationManager
                         .authenticate(Mono.just(new PlainTextUsernamePasswordAuthenticationRequest(username, password)))
+                        .switchIfEmpty(Mono.error(() -> new IllegalArgumentException("密码错误")))
                         .map(auth -> {
                             //触发授权成功事件
                             AuthorizationSuccessEvent event = new AuthorizationSuccessEvent(auth, parameterGetter);

+ 56 - 0
hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/basic/web/CompositeReactiveAuthenticationManagerTest.java

@@ -0,0 +1,56 @@
+package org.hswebframework.web.authorization.basic.web;
+
+import org.hswebframework.web.authorization.*;
+import org.hswebframework.web.authorization.simple.CompositeReactiveAuthenticationManager;
+import org.hswebframework.web.authorization.simple.PlainTextUsernamePasswordAuthenticationRequest;
+import org.hswebframework.web.authorization.simple.SimpleAuthentication;
+import org.hswebframework.web.authorization.simple.SimpleUser;
+import org.junit.Test;
+import reactor.core.publisher.Mono;
+import reactor.test.StepVerifier;
+
+import java.util.Arrays;
+
+
+public class CompositeReactiveAuthenticationManagerTest {
+
+    @Test
+    public void test() {
+        CompositeReactiveAuthenticationManager manager = new CompositeReactiveAuthenticationManager(
+                Arrays.asList(
+                        new ReactiveAuthenticationManagerProvider() {
+                            @Override
+                            public Mono<Authentication> authenticate(Mono<AuthenticationRequest> request) {
+                                return Mono.error(new IllegalArgumentException("密码错误"));
+                            }
+
+                            @Override
+                            public Mono<Authentication> getByUserId(String userId) {
+                                return Mono.empty();
+                            }
+                        },
+                        new ReactiveAuthenticationManagerProvider() {
+                            @Override
+                            public Mono<Authentication> authenticate(Mono<AuthenticationRequest> request) {
+                                SimpleAuthentication authentication = new SimpleAuthentication();
+                                authentication.setUser(SimpleUser.builder().id("test").build());
+
+                                return Mono.just(authentication);
+                            }
+
+                            @Override
+                            public Mono<Authentication> getByUserId(String userId) {
+                                return Mono.empty();
+                            }
+                        }
+                )
+        );
+
+        manager.authenticate(Mono.just(new PlainTextUsernamePasswordAuthenticationRequest()))
+                .map(Authentication::getUser)
+                .map(User::getId)
+                .as(StepVerifier::create)
+                .expectNext("test")
+                .verifyComplete();
+    }
+}

+ 5 - 1
hsweb-commons/hsweb-commons-api/src/main/java/org/hswebframework/web/api/crud/entity/EntityFactory.java

@@ -97,7 +97,11 @@ public interface EntityFactory {
      * @param <T>         泛型
      * @return 实体类型
      */
-    <T> Class<T> getInstanceType(Class<T> entityClass);
+   default  <T> Class<T> getInstanceType(Class<T> entityClass){
+       return getInstanceType(entityClass,false);
+   }
+
+    <T> Class<T> getInstanceType(Class<T> entityClass,boolean autoRegister);
 
     /**
      * 拷贝对象的属性

+ 0 - 2
hsweb-commons/hsweb-commons-api/src/main/java/org/hswebframework/web/api/crud/entity/SortSupportEntity.java

@@ -20,8 +20,6 @@ package org.hswebframework.web.api.crud.entity;
 
 public interface SortSupportEntity extends Comparable<SortSupportEntity>, Entity {
 
-    String sortIndex = "sortIndex";
-
     Long getSortIndex();
 
     void setSortIndex(Long sortIndex);

+ 21 - 13
hsweb-commons/hsweb-commons-api/src/main/java/org/hswebframework/web/api/crud/entity/TreeSupportEntity.java

@@ -78,6 +78,13 @@ public interface TreeSupportEntity<PK> extends Entity {
         }
     }
 
+    static <T extends TreeSupportEntity<PK>, PK> List<T> expandTree2List(T parent, IDGenerator<PK> idGenerator) {
+        List<T> list = new LinkedList<>();
+        expandTree2List(parent, list, idGenerator);
+
+        return list;
+    }
+
     static <T extends TreeSupportEntity<PK>, PK> void expandTree2List(T parent, List<T> target, IDGenerator<PK> idGenerator) {
         expandTree2List(parent, target, idGenerator, null);
     }
@@ -95,18 +102,6 @@ public interface TreeSupportEntity<PK> extends Entity {
      * @param <PK>        主键类型
      */
     static <T extends TreeSupportEntity<PK>, PK> void expandTree2List(T root, List<T> target, IDGenerator<PK> idGenerator, BiConsumer<T, List<T>> childConsumer) {
-
-        if (CollectionUtils.isEmpty(root.getChildren())) {
-            target.add(root);
-            return;
-        }
-
-        //尝试设置id
-        PK parentId = root.getId();
-        if (parentId == null) {
-            parentId = idGenerator.generate();
-            root.setId(parentId);
-        }
         //尝试设置树路径path
         if (root.getPath() == null) {
             root.setPath(RandomUtil.randomChar(4));
@@ -123,6 +118,18 @@ public interface TreeSupportEntity<PK> extends Entity {
             }
         }
 
+        if (CollectionUtils.isEmpty(root.getChildren())) {
+            target.add(root);
+            return;
+        }
+
+        //尝试设置id
+        PK parentId = root.getId();
+        if (parentId == null) {
+            parentId = idGenerator.generate();
+            root.setId(parentId);
+        }
+
         //所有节点处理队列
         Queue<T> queue = new LinkedList<>();
         queue.add(root);
@@ -199,12 +206,13 @@ public interface TreeSupportEntity<PK> extends Entity {
         Objects.requireNonNull(childConsumer, "child consumer can not be null");
         Objects.requireNonNull(predicateFunction, "root predicate function can not be null");
 
-        Supplier<Stream<N>> streamSupplier = () -> dataList.size() < 50000 ? dataList.stream() : dataList.parallelStream();
+        Supplier<Stream<N>> streamSupplier = () -> dataList.stream();
         // id,node
         Map<PK, N> cache = new HashMap<>();
         // parentId,children
         Map<PK, List<N>> treeCache = streamSupplier.get()
                 .peek(node -> cache.put(node.getId(), node))
+                .filter(e -> e.getParentId() != null)
                 .collect(Collectors.groupingBy(TreeSupportEntity::getParentId));
 
         Predicate<N> rootNodePredicate = predicateFunction.apply(new TreeHelper<N, PK>() {

+ 0 - 1
hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/configuration/AutoDDLProcessor.java

@@ -37,7 +37,6 @@ public class AutoDDLProcessor implements InitializingBean {
 
     private boolean reactive;
 
-
     @Override
     public void afterPropertiesSet() {
         if(entityFactory instanceof MapperEntityFactory){

+ 1 - 1
hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/configuration/EasyOrmConfiguration.java

@@ -62,7 +62,7 @@ public class EasyOrmConfiguration {
             @Override
             public EntityColumnMapping getMapping(Class entity) {
 
-                return resolver.resolve(entityFactory.getInstanceType(entity))
+                return resolver.resolve(entityFactory.getInstanceType(entity,true))
                         .getFeature(MappingFeatureType.columnPropertyMapping.createFeatureId(entity))
                         .map(EntityColumnMapping.class::cast)
                         .orElse(null);

+ 23 - 14
hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/entity/factory/MapperEntityFactory.java

@@ -13,7 +13,7 @@
  *  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  *  * See the License for the specific language governing permissions and
  *  * limitations under the License.
- *  
+ *
  */
 
 package org.hswebframework.web.crud.entity.factory;
@@ -37,9 +37,9 @@ import java.util.function.Supplier;
  */
 @SuppressWarnings("unchecked")
 public class MapperEntityFactory implements EntityFactory, BeanFactory {
-    private Map<Class, Mapper>          realTypeMapper = new HashMap<>();
-    private Logger                      logger         = LoggerFactory.getLogger(this.getClass());
-    private Map<String, PropertyCopier> copierCache    = new HashMap<>();
+    private Map<Class, Mapper> realTypeMapper = new HashMap<>();
+    private Logger logger = LoggerFactory.getLogger(this.getClass());
+    private Map<String, PropertyCopier> copierCache = new HashMap<>();
 
     private static final DefaultMapperFactory DEFAULT_MAPPER_FACTORY = clazz -> {
         String simpleClassName = clazz.getPackage().getName().concat(".Simple").concat(clazz.getSimpleName());
@@ -64,7 +64,7 @@ public class MapperEntityFactory implements EntityFactory, BeanFactory {
     public MapperEntityFactory() {
     }
 
-    public   MapperEntityFactory(Map<Class<?>, Mapper> realTypeMapper) {
+    public MapperEntityFactory(Map<Class<?>, Mapper> realTypeMapper) {
         this.realTypeMapper.putAll(realTypeMapper);
     }
 
@@ -175,19 +175,28 @@ public class MapperEntityFactory implements EntityFactory, BeanFactory {
 
     @Override
     @SuppressWarnings("unchecked")
-    public <T> Class<T> getInstanceType(Class<T> beanClass) {
+    public <T> Class<T> getInstanceType(Class<T> beanClass, boolean autoRegister) {
+        if (beanClass == null
+                || beanClass.isPrimitive()
+                || beanClass.isArray()
+                || beanClass.isEnum()) {
+            return null;
+        }
         Mapper<T> mapper = realTypeMapper.get(beanClass);
         if (null != mapper) {
             return mapper.getTarget();
         }
-        mapper = initCache(beanClass);
-        if (mapper != null) {
-            return mapper.getTarget();
-        }
+        if (autoRegister) {
+            mapper = initCache(beanClass);
+            if (mapper != null) {
+                return mapper.getTarget();
+            }
 
-        return Modifier.isAbstract(beanClass.getModifiers())
-                || Modifier.isInterface(beanClass.getModifiers())
-                ? null : beanClass;
+            return Modifier.isAbstract(beanClass.getModifiers())
+                    || Modifier.isInterface(beanClass.getModifiers())
+                    ? null : beanClass;
+        }
+        return null;
     }
 
     public void setDefaultMapperFactory(DefaultMapperFactory defaultMapperFactory) {
@@ -200,7 +209,7 @@ public class MapperEntityFactory implements EntityFactory, BeanFactory {
     }
 
     public static class Mapper<T> {
-        Class<T>    target;
+        Class<T> target;
         Supplier<T> instanceGetter;
 
         public Mapper(Class<T> target, Supplier<T> instanceGetter) {

+ 3 - 1
hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/generator/DefaultIdGenerator.java

@@ -7,13 +7,14 @@ import lombok.extern.slf4j.Slf4j;
 import org.hswebframework.ezorm.core.DefaultValue;
 import org.hswebframework.ezorm.core.DefaultValueGenerator;
 import org.hswebframework.ezorm.rdb.metadata.RDBColumnMetadata;
+import org.hswebframework.web.id.IDGenerator;
 import reactor.core.publisher.Mono;
 
 import java.util.HashMap;
 import java.util.Map;
 
 @Slf4j
-public class DefaultIdGenerator implements DefaultValueGenerator<RDBColumnMetadata> {
+public class DefaultIdGenerator implements DefaultValueGenerator<RDBColumnMetadata>  {
 
     @Getter
     @Setter
@@ -45,4 +46,5 @@ public class DefaultIdGenerator implements DefaultValueGenerator<RDBColumnMetada
     public String getName() {
         return "默认ID生成器";
     }
+
 }

+ 2 - 0
hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/generator/MD5Generator.java

@@ -20,4 +20,6 @@ public class MD5Generator implements DefaultValueGenerator<RDBColumnMetadata> {
     public String getName() {
         return "MD5";
     }
+
+
 }

+ 10 - 5
hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/service/ReactiveCrudService.java

@@ -42,26 +42,31 @@ public interface ReactiveCrudService<E, K> {
         return publisher.flatMap(e -> findById(Mono.just(e)));
     }
 
-    @Transactional(rollbackFor = Throwable.class)
+    @Transactional
     default Mono<SaveResult> save(Publisher<E> entityPublisher) {
         return getRepository()
                 .save(entityPublisher);
     }
 
-    @Transactional(rollbackFor = Throwable.class)
+    @Transactional
+    default Mono<Integer> updateById(K id, Mono<E> entityPublisher) {
+        return getRepository()
+                .updateById(id, entityPublisher);
+    }
+
+    @Transactional
     default Mono<Integer> insertBatch(Publisher<? extends Collection<E>> entityPublisher) {
         return getRepository()
                 .insertBatch(entityPublisher);
     }
 
-    @Transactional(rollbackFor = Throwable.class)
+    @Transactional
     default Mono<Integer> insert(Publisher<E> entityPublisher) {
         return getRepository()
                 .insert(entityPublisher);
     }
 
-
-    @Transactional(rollbackFor = Throwable.class)
+    @Transactional
     default Mono<Integer> deleteById(Publisher<K> idPublisher) {
         return getRepository()
                 .deleteById(idPublisher);

+ 76 - 0
hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/service/ReactiveTreeSortEntityService.java

@@ -0,0 +1,76 @@
+package org.hswebframework.web.crud.service;
+
+import org.hswebframework.ezorm.core.param.QueryParam;
+import org.hswebframework.ezorm.rdb.mapping.defaults.SaveResult;
+import org.hswebframework.web.api.crud.entity.QueryParamEntity;
+import org.hswebframework.web.api.crud.entity.TreeSortSupportEntity;
+import org.hswebframework.web.api.crud.entity.TreeSupportEntity;
+import org.hswebframework.web.id.IDGenerator;
+import org.reactivestreams.Publisher;
+import org.springframework.util.StringUtils;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+public interface ReactiveTreeSortEntityService<E extends TreeSortSupportEntity<K>, K>
+        extends ReactiveCrudService<E, K> {
+
+    default Mono<List<E>> queryResultToTree(Mono<? extends QueryParam> paramEntity) {
+        return query(paramEntity)
+                .collectList()
+                .map(list -> TreeSupportEntity.list2tree(list, this::setChildren, this::isRootNode));
+    }
+
+    @Override
+    default Mono<Integer> insert(Publisher<E> entityPublisher) {
+        return insertBatch(Flux.from(entityPublisher).collectList());
+    }
+
+    @Override
+    default Mono<Integer> insertBatch(Publisher<? extends Collection<E>> entityPublisher) {
+        return this.getRepository()
+                .insertBatch(Flux.from(entityPublisher)
+                .flatMap(Flux::fromIterable)
+                .flatMap(e -> Flux.fromIterable(TreeSupportEntity.expandTree2List(e, getIDGenerator())))
+                .collectList());
+    }
+
+    @Override
+    default Mono<SaveResult> save(Publisher<E> entityPublisher) {
+        return this.getRepository()
+                .save(Flux.from(entityPublisher)
+                        //把树结构平铺
+                        .flatMap(e -> Flux.fromIterable(TreeSupportEntity.expandTree2List(e, getIDGenerator()))));
+    }
+
+    @Override
+    default Mono<Integer> updateById(K id, Mono<E> entityPublisher) {
+        return save(entityPublisher
+                .map(e -> {
+                    e.setId(id);
+                    return e;
+                })).map(SaveResult::getTotal);
+    }
+
+    @Override
+    default Mono<Integer> deleteById(Publisher<K> idPublisher) {
+        return findById(Flux.from(idPublisher))
+                .flatMap(e -> createDelete()
+                        .where()
+                        .like$(e::getPath)
+                        .execute())
+                .collect(Collectors.summingInt(Integer::intValue));
+    }
+
+    IDGenerator<K> getIDGenerator();
+
+    void setChildren(E entity, List<E> children);
+
+    List<E> getChildren(E entity);
+
+   default boolean isRootNode(E entity){
+       return StringUtils.isEmpty(entity.getParentId()) || "-1".equals(String.valueOf(entity.getParentId()));
+   }
+}

+ 8 - 8
hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/CommonErrorControllerAdvice.java

@@ -79,7 +79,7 @@ public class CommonErrorControllerAdvice {
                 .stream()
                 .filter(FieldError.class::isInstance)
                 .map(FieldError.class::cast)
-                .map(err -> new ValidationException.Detail(err.getField(), err.getDefaultMessage()))
+                .map(err -> new ValidationException.Detail(err.getField(), err.getDefaultMessage(),null))
                 .collect(Collectors.toList())));
     }
 
@@ -90,7 +90,7 @@ public class CommonErrorControllerAdvice {
                 .stream()
                 .filter(FieldError.class::isInstance)
                 .map(FieldError.class::cast)
-                .map(err -> new ValidationException.Detail(err.getField(), err.getDefaultMessage()))
+                .map(err -> new ValidationException.Detail(err.getField(), err.getDefaultMessage(),null))
                 .collect(Collectors.toList())));
     }
 
@@ -102,7 +102,7 @@ public class CommonErrorControllerAdvice {
                 .stream()
                 .filter(FieldError.class::isInstance)
                 .map(FieldError.class::cast)
-                .map(err -> new ValidationException.Detail(err.getField(), err.getDefaultMessage()))
+                .map(err -> new ValidationException.Detail(err.getField(), err.getDefaultMessage(),null))
                 .collect(Collectors.toList())));
     }
 
@@ -122,28 +122,28 @@ public class CommonErrorControllerAdvice {
     @ExceptionHandler
     @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
     public Mono<ResponseMessage<?>> handleException(RuntimeException e) {
-        log.error(e.getMessage());
+        log.error(e.getMessage(), e);
         return Mono.just(ResponseMessage.error(e.getMessage()));
     }
 
     @ExceptionHandler
     @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
     public Mono<ResponseMessage<?>> handleException(NullPointerException e) {
-        log.error(e.getMessage());
+        log.error(e.getMessage(), e);
         return Mono.just(ResponseMessage.error(e.getMessage()));
     }
 
     @ExceptionHandler
     @ResponseStatus(HttpStatus.BAD_REQUEST)
     public Mono<ResponseMessage<?>> handleException(IllegalArgumentException e) {
-        log.error(e.getMessage());
-        return Mono.just(ResponseMessage.error("illegal_argument", e.getMessage()));
+        log.error(e.getMessage(), e);
+        return Mono.just(ResponseMessage.error(400,"illegal_argument", e.getMessage()));
     }
 
     @ExceptionHandler
     @ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
     public Mono<ResponseMessage<?>> handleException(MediaTypeNotSupportedStatusException e) {
-        log.error(e.getMessage());
+        log.error(e.getMessage(), e);
         return Mono.just(ResponseMessage
                 .error(415, "unsupported_media_type", "不支持的请求类型")
                 .result(e.getSupportedMediaTypes()));

+ 2 - 0
hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/CommonWebFluxConfiguration.java

@@ -1,5 +1,6 @@
 package org.hswebframework.web.crud.web;
 
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
 import org.springframework.context.annotation.Bean;
@@ -13,6 +14,7 @@ import org.springframework.web.reactive.accept.RequestedContentTypeResolver;
 public class CommonWebFluxConfiguration {
 
     @Bean
+    @ConditionalOnMissingBean
     public CommonErrorControllerAdvice commonErrorControllerAdvice(){
         return new CommonErrorControllerAdvice();
     }

+ 4 - 0
hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/reactive/ReactiveDeleteController.java

@@ -1,15 +1,19 @@
 package org.hswebframework.web.crud.web.reactive;
 
 import org.hswebframework.ezorm.rdb.mapping.ReactiveRepository;
+import org.hswebframework.web.authorization.annotation.Authorize;
+import org.hswebframework.web.authorization.annotation.DeleteAction;
 import org.hswebframework.web.exception.NotFoundException;
 import org.springframework.web.bind.annotation.DeleteMapping;
 import org.springframework.web.bind.annotation.PathVariable;
 import reactor.core.publisher.Mono;
 
 public interface ReactiveDeleteController<E, K> {
+    @Authorize(ignore = true)
     ReactiveRepository<E, K> getRepository();
 
     @DeleteMapping("/{id:.+}")
+    @DeleteAction
     default Mono<E> delete(@PathVariable K id) {
         return getRepository()
                 .findById(Mono.just(id))

+ 9 - 7
hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/reactive/ReactiveQueryController.java

@@ -6,6 +6,7 @@ import org.hswebframework.web.api.crud.entity.PagerResult;
 import org.hswebframework.web.api.crud.entity.QueryParamEntity;
 import org.hswebframework.web.authorization.Permission;
 import org.hswebframework.web.authorization.annotation.Authorize;
+import org.hswebframework.web.authorization.annotation.QueryAction;
 import org.hswebframework.web.exception.NotFoundException;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PathVariable;
@@ -17,10 +18,11 @@ import java.util.Collections;
 
 public interface ReactiveQueryController<E, K> {
 
+    @Authorize(ignore = true)
     ReactiveRepository<E, K> getRepository();
 
     @GetMapping("/_query/no-paging")
-    @Authorize(action = Permission.ACTION_QUERY)
+    @QueryAction
     default Flux<E> query(QueryParamEntity query) {
         return getRepository()
                 .createQuery()
@@ -29,13 +31,13 @@ public interface ReactiveQueryController<E, K> {
     }
 
     @PostMapping("/_query/no-paging")
-    @Authorize(action = Permission.ACTION_QUERY)
+    @QueryAction
     default Flux<E> query(Mono<QueryParamEntity> query) {
         return query.flatMapMany(this::query);
     }
 
     @GetMapping("/_count")
-    @Authorize(action = Permission.ACTION_QUERY)
+    @QueryAction
     default Mono<Integer> count(QueryParamEntity query) {
         return getRepository()
                 .createQuery()
@@ -44,13 +46,13 @@ public interface ReactiveQueryController<E, K> {
     }
 
     @GetMapping("/_query")
-    @Authorize(action = Permission.ACTION_QUERY)
+    @QueryAction
     default Mono<PagerResult<E>> queryPager(QueryParamEntity query) {
         return queryPager(Mono.just(query));
     }
 
     @PostMapping("/_query")
-    @Authorize(action = Permission.ACTION_QUERY)
+    @QueryAction
     @SuppressWarnings("all")
     default Mono<PagerResult<E>> queryPager(Mono<QueryParamEntity> query) {
         return count(query)
@@ -65,13 +67,13 @@ public interface ReactiveQueryController<E, K> {
     }
 
     @PostMapping("/_count")
-    @Authorize(action = Permission.ACTION_QUERY)
+    @QueryAction
     default Mono<Integer> count(Mono<QueryParamEntity> query) {
         return query.flatMap(this::count);
     }
 
     @GetMapping("/{id:.+}")
-    @Authorize(action = Permission.ACTION_GET)
+    @QueryAction
     default Mono<E> getById(@PathVariable K id) {
         return getRepository()
                 .findById(Mono.just(id))

+ 8 - 3
hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/reactive/ReactiveSaveController.java

@@ -6,6 +6,7 @@ import org.hswebframework.web.api.crud.entity.RecordModifierEntity;
 import org.hswebframework.web.authorization.Authentication;
 import org.hswebframework.web.authorization.Permission;
 import org.hswebframework.web.authorization.annotation.Authorize;
+import org.hswebframework.web.authorization.annotation.SaveAction;
 import org.springframework.web.bind.annotation.*;
 import reactor.core.publisher.Mono;
 
@@ -13,8 +14,10 @@ import javax.validation.Valid;
 
 public interface ReactiveSaveController<E, K> {
 
+    @Authorize(ignore = true)
     ReactiveRepository<E, K> getRepository();
 
+    @Authorize(ignore = true)
     default E applyCreationEntity(Authentication authentication, E entity) {
         RecordCreationEntity creationEntity = ((RecordCreationEntity) entity);
         creationEntity.setCreateTimeNow();
@@ -23,6 +26,7 @@ public interface ReactiveSaveController<E, K> {
         return entity;
     }
 
+    @Authorize(ignore = true)
     default E applyModifierEntity(Authentication authentication, E entity) {
         RecordModifierEntity creationEntity = ((RecordModifierEntity) entity);
         creationEntity.setModifyTimeNow();
@@ -31,6 +35,7 @@ public interface ReactiveSaveController<E, K> {
         return entity;
     }
 
+    @Authorize(ignore = true)
     default E applyAuthentication(Authentication authentication, E entity) {
         if (entity instanceof RecordCreationEntity) {
             entity = applyCreationEntity(authentication, entity);
@@ -42,7 +47,7 @@ public interface ReactiveSaveController<E, K> {
     }
 
     @PatchMapping
-    @Authorize(action = Permission.ACTION_UPDATE)
+    @SaveAction
     default Mono<E> save(@RequestBody Mono<E> payload) {
         return Authentication.currentReactive()
                 .zipWith(payload, this::applyAuthentication)
@@ -51,7 +56,7 @@ public interface ReactiveSaveController<E, K> {
     }
 
     @PostMapping
-    @Authorize(action = Permission.ACTION_UPDATE)
+    @SaveAction
     default Mono<E> add(@RequestBody Mono<E> payload) {
         return  Authentication.currentReactive()
                 .zipWith(payload, this::applyAuthentication)
@@ -60,7 +65,7 @@ public interface ReactiveSaveController<E, K> {
     }
 
     @PutMapping("/{id}")
-    @Authorize(action = Permission.ACTION_UPDATE)
+    @SaveAction
     default Mono<E> update(@PathVariable K id, @RequestBody Mono<E> payload) {
         return  Authentication.currentReactive()
                 .zipWith(payload, this::applyAuthentication)

+ 9 - 0
hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/reactive/ReactiveServiceCrudController.java

@@ -0,0 +1,9 @@
+package org.hswebframework.web.crud.web.reactive;
+
+public interface ReactiveServiceCrudController<E, K> extends
+        ReactiveServiceSaveController<E, K>,
+        ReactiveServiceQueryController<E, K>,
+        ReactiveServiceDeleteController<E, K> {
+
+
+}

+ 25 - 0
hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/reactive/ReactiveServiceDeleteController.java

@@ -0,0 +1,25 @@
+package org.hswebframework.web.crud.web.reactive;
+
+import org.hswebframework.web.authorization.annotation.Authorize;
+import org.hswebframework.web.authorization.annotation.DeleteAction;
+import org.hswebframework.web.crud.service.ReactiveCrudService;
+import org.hswebframework.web.exception.NotFoundException;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import reactor.core.publisher.Mono;
+
+public interface ReactiveServiceDeleteController<E, K> {
+    @Authorize(ignore = true)
+    ReactiveCrudService<E, K> getService();
+
+    @DeleteMapping("/{id:.+}")
+    @DeleteAction
+    default Mono<E> delete(@PathVariable K id) {
+        return getService()
+                .findById(Mono.just(id))
+                .switchIfEmpty(Mono.error(NotFoundException::new))
+                .flatMap(e -> getService()
+                        .deleteById(Mono.just(id))
+                        .thenReturn(e));
+    }
+}

+ 83 - 0
hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/reactive/ReactiveServiceQueryController.java

@@ -0,0 +1,83 @@
+package org.hswebframework.web.crud.web.reactive;
+
+import org.hswebframework.ezorm.core.param.QueryParam;
+import org.hswebframework.web.api.crud.entity.PagerResult;
+import org.hswebframework.web.api.crud.entity.QueryParamEntity;
+import org.hswebframework.web.authorization.annotation.Authorize;
+import org.hswebframework.web.authorization.annotation.QueryAction;
+import org.hswebframework.web.crud.service.ReactiveCrudService;
+import org.hswebframework.web.exception.NotFoundException;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+import java.util.Collections;
+
+public interface ReactiveServiceQueryController<E, K> {
+
+    @Authorize(ignore = true)
+    ReactiveCrudService<E, K> getService();
+
+    @GetMapping("/_query/no-paging")
+    @QueryAction
+    default Flux<E> query(QueryParamEntity query) {
+        return getService()
+                .createQuery()
+                .setParam(query)
+                .fetch();
+    }
+
+    @PostMapping("/_query/no-paging")
+    @QueryAction
+    default Flux<E> query(Mono<QueryParamEntity> query) {
+        return query.flatMapMany(this::query);
+    }
+
+    @GetMapping("/_count")
+    @QueryAction
+    default Mono<Integer> count(QueryParamEntity query) {
+        return getService()
+                .createQuery()
+                .setParam(query)
+                .count();
+    }
+
+    @GetMapping("/_query")
+    @QueryAction
+    default Mono<PagerResult<E>> queryPager(QueryParamEntity query) {
+        return queryPager(Mono.just(query));
+    }
+
+    @PostMapping("/_query")
+    @QueryAction
+    @SuppressWarnings("all")
+    default Mono<PagerResult<E>> queryPager(Mono<QueryParamEntity> query) {
+        return count(query)
+                .zipWhen(total -> {
+                    if (total == 0) {
+                        return Mono.just(Collections.<E>emptyList());
+                    }
+                    return query
+                            .map(QueryParam::clone)
+                            .flatMap(q -> query(Mono.just(q.rePaging(total))).collectList());
+                }, PagerResult::of);
+    }
+
+    @PostMapping("/_count")
+    @QueryAction
+    default Mono<Integer> count(Mono<QueryParamEntity> query) {
+        return query.flatMap(this::count);
+    }
+
+    @GetMapping("/{id:.+}")
+    @QueryAction
+    default Mono<E> getById(@PathVariable K id) {
+        return getService()
+                .findById(Mono.just(id))
+                .switchIfEmpty(Mono.error(NotFoundException::new));
+    }
+
+
+}

+ 72 - 0
hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/web/reactive/ReactiveServiceSaveController.java

@@ -0,0 +1,72 @@
+package org.hswebframework.web.crud.web.reactive;
+
+import org.hswebframework.web.api.crud.entity.RecordCreationEntity;
+import org.hswebframework.web.api.crud.entity.RecordModifierEntity;
+import org.hswebframework.web.authorization.Authentication;
+import org.hswebframework.web.authorization.annotation.Authorize;
+import org.hswebframework.web.authorization.annotation.SaveAction;
+import org.hswebframework.web.crud.service.ReactiveCrudService;
+import org.springframework.web.bind.annotation.*;
+import reactor.core.publisher.Mono;
+
+public interface ReactiveServiceSaveController<E,K>  {
+
+    @Authorize(ignore = true)
+    ReactiveCrudService<E,K> getService();
+
+    @Authorize(ignore = true)
+    default E applyCreationEntity(Authentication authentication, E entity) {
+        RecordCreationEntity creationEntity = ((RecordCreationEntity) entity);
+        creationEntity.setCreateTimeNow();
+        creationEntity.setCreatorId(authentication.getUser().getId());
+
+        return entity;
+    }
+
+    @Authorize(ignore = true)
+    default E applyModifierEntity(Authentication authentication, E entity) {
+        RecordModifierEntity creationEntity = ((RecordModifierEntity) entity);
+        creationEntity.setModifyTimeNow();
+        creationEntity.setModifierId(authentication.getUser().getId());
+
+        return entity;
+    }
+
+    @Authorize(ignore = true)
+    default E applyAuthentication(Authentication authentication, E entity) {
+        if (entity instanceof RecordCreationEntity) {
+            entity = applyCreationEntity(authentication, entity);
+        }
+        if (entity instanceof RecordModifierEntity) {
+            entity = applyModifierEntity(authentication, entity);
+        }
+        return entity;
+    }
+
+    @PatchMapping
+    @SaveAction
+    default Mono<E> save(@RequestBody Mono<E> payload) {
+        return Authentication.currentReactive()
+                .zipWith(payload, this::applyAuthentication)
+                .switchIfEmpty(payload)
+                .flatMap(entity -> getService().save(Mono.just(entity)).thenReturn(entity));
+    }
+
+    @PostMapping
+    @SaveAction
+    default Mono<E> add(@RequestBody Mono<E> payload) {
+        return  Authentication.currentReactive()
+                .zipWith(payload, this::applyAuthentication)
+                .switchIfEmpty(payload)
+                .flatMap(entity -> getService().insert(Mono.just(entity)).thenReturn(entity));
+    }
+
+    @PutMapping("/{id}")
+    @SaveAction
+    default Mono<E> update(@PathVariable K id, @RequestBody Mono<E> payload) {
+        return  Authentication.currentReactive()
+                .zipWith(payload, this::applyAuthentication)
+                .switchIfEmpty(payload)
+                .flatMap(entity -> getService().updateById(id,Mono.just(entity)).thenReturn(entity));
+    }
+}

+ 25 - 0
hsweb-commons/hsweb-commons-crud/src/test/java/org/hswebframework/web/crud/entity/TestTreeSortEntity.java

@@ -0,0 +1,25 @@
+package org.hswebframework.web.crud.entity;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.hswebframework.web.api.crud.entity.GenericTreeSortSupportEntity;
+import org.hswebframework.web.api.crud.entity.TreeSupportEntity;
+
+import javax.persistence.Column;
+import javax.persistence.Table;
+import java.util.List;
+
+@Getter
+@Setter
+@Table(name = "test_tree_sort")
+public class TestTreeSortEntity  extends GenericTreeSortSupportEntity<String> {
+
+
+    @Column
+    private String name;
+
+
+    private List<TestTreeSortEntity> children;
+
+
+}

+ 62 - 0
hsweb-commons/hsweb-commons-crud/src/test/java/org/hswebframework/web/crud/service/ReactiveTreeSortEntityServiceTest.java

@@ -0,0 +1,62 @@
+package org.hswebframework.web.crud.service;
+
+import org.hswebframework.ezorm.core.param.QueryParam;
+import org.hswebframework.ezorm.rdb.mapping.defaults.SaveResult;
+import org.hswebframework.web.crud.entity.TestTreeSortEntity;
+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.context.junit4.SpringJUnit4ClassRunner;
+import reactor.core.publisher.Mono;
+import reactor.test.StepVerifier;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.Assert.*;
+
+@SpringBootTest
+@RunWith(SpringJUnit4ClassRunner.class)
+public class ReactiveTreeSortEntityServiceTest {
+
+    @Autowired
+    private TestTreeSortEntityService sortEntityService;
+
+
+    @Test
+    public void testCrud(){
+        TestTreeSortEntity entity=new TestTreeSortEntity();
+        entity.setId("test");
+        entity.setName("test");
+
+        TestTreeSortEntity entity2=new TestTreeSortEntity();
+        entity2.setName("test2");
+
+        entity.setChildren(Arrays.asList(entity2));
+
+        sortEntityService.insert(Mono.just(entity))
+                .as(StepVerifier::create)
+                .expectNext(2)
+                .verifyComplete();
+
+        sortEntityService.save(Mono.just(entity))
+                .map(SaveResult::getTotal)
+                .as(StepVerifier::create)
+                .expectNext(2)
+                .verifyComplete();
+
+        sortEntityService.queryResultToTree(Mono.just(new QueryParam()))
+                .map(List::size)
+                .as(StepVerifier::create)
+                .expectNext(1)
+                .verifyComplete();
+
+
+        sortEntityService.deleteById(Mono.just("test"))
+                .as(StepVerifier::create)
+                .expectNext(2)
+                .verifyComplete();
+    }
+
+}

+ 33 - 0
hsweb-commons/hsweb-commons-crud/src/test/java/org/hswebframework/web/crud/service/TestTreeSortEntityService.java

@@ -0,0 +1,33 @@
+package org.hswebframework.web.crud.service;
+
+import org.hswebframework.web.crud.entity.TestTreeSortEntity;
+import org.hswebframework.web.id.IDGenerator;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Service
+public class TestTreeSortEntityService extends GenericReactiveCrudService<TestTreeSortEntity,String>
+        implements ReactiveTreeSortEntityService<TestTreeSortEntity,String> {
+
+    @Override
+    public IDGenerator<String> getIDGenerator() {
+        return IDGenerator.MD5;
+    }
+
+    @Override
+    public void setChildren(TestTreeSortEntity entity, List<TestTreeSortEntity> children) {
+        entity.setChildren(children);
+    }
+
+    @Override
+    public List<TestTreeSortEntity> getChildren(TestTreeSortEntity entity) {
+        return entity.getChildren();
+    }
+
+    @Override
+    public boolean isRootNode(TestTreeSortEntity entity) {
+        return entity.getParentId()==null;
+    }
+
+}

+ 4 - 10
hsweb-datasource/hsweb-datasource-web/src/main/java/org/hswebframework/web/datasource/web/DatasourceController.java

@@ -1,9 +1,7 @@
 package org.hswebframework.web.datasource.web;
 
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
-import org.hswebframework.web.authorization.Permission;
-import org.hswebframework.web.authorization.annotation.Authorize;
+import org.hswebframework.web.authorization.annotation.QueryAction;
+import org.hswebframework.web.authorization.annotation.Resource;
 import org.hswebframework.web.datasource.config.DynamicDataSourceConfig;
 import org.hswebframework.web.datasource.config.DynamicDataSourceConfigRepository;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -15,18 +13,14 @@ import java.util.List;
 
 @RestController
 @RequestMapping("/datasource")
-@Api(tags = "开发人员工具-数据源", value = "数据源")
-@Authorize(permission =
-    @Authorize.Resource(value = "datasource", description = "数据源管理")
-)
+@Resource(id = "datasource", name = "数据源管理")
 public class DatasourceController {
 
     @Autowired
     private DynamicDataSourceConfigRepository<? extends DynamicDataSourceConfig> repository;
 
     @GetMapping
-    @Authorize(action = Permission.ACTION_QUERY)
-    @ApiOperation("获取全部数据源信息")
+    @QueryAction
     public List<? extends DynamicDataSourceConfig> getAllConfig() {
         return repository.findAll();
     }

+ 4 - 0
hsweb-starter/pom.xml

@@ -27,5 +27,9 @@
             <artifactId>hsweb-commons-crud</artifactId>
             <version>${project.version}</version>
         </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-webflux</artifactId>
+        </dependency>
     </dependencies>
 </project>

+ 37 - 0
hsweb-starter/src/main/java/org/hswebframework/web/starter/jackson/CustomCodecsAutoConfiguration.java

@@ -0,0 +1,37 @@
+package org.hswebframework.web.starter.jackson;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.hswebframework.web.api.crud.entity.EntityFactory;
+import org.springframework.boot.autoconfigure.AutoConfigureAfter;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
+import org.springframework.boot.web.codec.CodecCustomizer;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.annotation.Order;
+import org.springframework.http.codec.CodecConfigurer;
+
+@Configuration(proxyBeanMethods = false)
+@AutoConfigureAfter(JacksonAutoConfiguration.class)
+public class CustomCodecsAutoConfiguration {
+
+	@Configuration(proxyBeanMethods = false)
+	@ConditionalOnClass(ObjectMapper.class)
+	static class JacksonDecoderConfiguration {
+
+		@Bean
+		@Order(1)
+		@ConditionalOnBean(ObjectMapper.class)
+		CodecCustomizer jacksonDecoderCustomizer(EntityFactory entityFactory, ObjectMapper objectMapper) {
+			objectMapper.setTypeFactory(new CustomTypeFactory(entityFactory));
+			return (configurer) -> {
+				CodecConfigurer.DefaultCodecs defaults = configurer.defaultCodecs();
+				defaults.jackson2JsonDecoder(new CustomJackson2JsonDecoder(objectMapper));
+			};
+		}
+
+	}
+
+
+}

+ 155 - 0
hsweb-starter/src/main/java/org/hswebframework/web/starter/jackson/CustomJackson2JsonDecoder.java

@@ -0,0 +1,155 @@
+package org.hswebframework.web.starter.jackson;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.ObjectReader;
+import com.fasterxml.jackson.databind.exc.InvalidDefinitionException;
+import com.fasterxml.jackson.databind.util.TokenBuffer;
+import org.reactivestreams.Publisher;
+import org.springframework.core.MethodParameter;
+import org.springframework.core.ResolvableType;
+import org.springframework.core.codec.CodecException;
+import org.springframework.core.codec.DecodingException;
+import org.springframework.core.codec.Hints;
+import org.springframework.core.io.buffer.DataBuffer;
+import org.springframework.core.io.buffer.DataBufferUtils;
+import org.springframework.core.log.LogFormatUtils;
+import org.springframework.http.codec.HttpMessageDecoder;
+import org.springframework.http.codec.json.Jackson2CodecSupport;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.http.server.reactive.ServerHttpResponse;
+import org.springframework.lang.Nullable;
+import org.springframework.util.Assert;
+import org.springframework.util.MimeType;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.List;
+import java.util.Map;
+
+public class CustomJackson2JsonDecoder extends Jackson2CodecSupport implements HttpMessageDecoder<Object> {
+
+    /**
+     * Constructor with a Jackson {@link ObjectMapper} to use.
+     */
+    public CustomJackson2JsonDecoder(ObjectMapper mapper, MimeType... mimeTypes) {
+        super(mapper, mimeTypes);
+    }
+
+
+    @Override
+    public boolean canDecode(ResolvableType elementType, @Nullable MimeType mimeType) {
+        JavaType javaType = getObjectMapper().getTypeFactory().constructType(elementType.getType());
+        // Skip String: CharSequenceDecoder + "*/*" comes after
+        return (!CharSequence.class.isAssignableFrom(elementType.toClass()) &&
+                getObjectMapper().canDeserialize(javaType) && supportsMimeType(mimeType));
+    }
+
+    @Override
+    public Flux<Object> decode(Publisher<DataBuffer> input, ResolvableType elementType,
+                               @Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
+
+        ObjectMapper mapper = getObjectMapper();
+        Flux<TokenBuffer> tokens = Jackson2Tokenizer.tokenize(
+                Flux.from(input), mapper.getFactory(), mapper, true);
+
+        ObjectReader reader = getObjectReader(elementType, hints);
+
+        return tokens.handle((tokenBuffer, sink) -> {
+            try {
+                Object value = reader.readValue(tokenBuffer.asParser(getObjectMapper()));
+                logValue(value, hints);
+                if (value != null) {
+                    sink.next(value);
+                }
+            } catch (IOException ex) {
+                sink.error(processException(ex));
+            }
+        });
+    }
+
+    @Override
+    public Mono<Object> decodeToMono(Publisher<DataBuffer> input, ResolvableType elementType,
+                                     @Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
+
+        return DataBufferUtils.join(input)
+                .map(dataBuffer -> decode(dataBuffer, elementType, mimeType, hints));
+    }
+
+    @Override
+    public Object decode(DataBuffer dataBuffer, ResolvableType targetType,
+                         @Nullable MimeType mimeType, @Nullable Map<String, Object> hints) throws DecodingException {
+
+        try {
+            ObjectReader objectReader = getObjectReader(targetType, hints);
+            Object value = objectReader.readValue(dataBuffer.asInputStream());
+            logValue(value, hints);
+            return value;
+        } catch (IOException ex) {
+            throw processException(ex);
+        } finally {
+            DataBufferUtils.release(dataBuffer);
+        }
+    }
+
+    private ObjectReader getObjectReader(ResolvableType elementType, @Nullable Map<String, Object> hints) {
+        Assert.notNull(elementType, "'elementType' must not be null");
+        MethodParameter param = getParameter(elementType);
+        Class<?> contextClass = (param != null ? param.getContainingClass() : null);
+        Type type = elementType.resolve() == null ? elementType.getType() : elementType.resolve();
+
+        JavaType javaType = getJavaType(type, contextClass);
+        Class<?> jsonView = (hints != null ? (Class<?>) hints.get(Jackson2CodecSupport.JSON_VIEW_HINT) : null);
+        return jsonView != null ?
+                getObjectMapper().readerWithView(jsonView).forType(javaType) :
+                getObjectMapper().readerFor(javaType);
+    }
+
+    private void logValue(@Nullable Object value, @Nullable Map<String, Object> hints) {
+        if (!Hints.isLoggingSuppressed(hints)) {
+            LogFormatUtils.traceDebug(logger, traceOn -> {
+                String formatted = LogFormatUtils.formatValue(value, !traceOn);
+                return Hints.getLogPrefix(hints) + "Decoded [" + formatted + "]";
+            });
+        }
+    }
+
+    private CodecException processException(IOException ex) {
+        if (ex instanceof InvalidDefinitionException) {
+            JavaType type = ((InvalidDefinitionException) ex).getType();
+            return new CodecException("Type definition error: " + type, ex);
+        }
+        if (ex instanceof JsonProcessingException) {
+            String originalMessage = ((JsonProcessingException) ex).getOriginalMessage();
+            return new DecodingException("JSON decoding error: " + originalMessage, ex);
+        }
+        return new DecodingException("I/O error while parsing input stream", ex);
+    }
+
+
+    // HttpMessageDecoder...
+
+    @Override
+    public Map<String, Object> getDecodeHints(ResolvableType actualType, ResolvableType elementType,
+                                              ServerHttpRequest request, ServerHttpResponse response) {
+
+        return getHints(actualType);
+    }
+
+    @Override
+    public List<MimeType> getDecodableMimeTypes() {
+        return getMimeTypes();
+    }
+
+    // Jackson2CodecSupport ...
+
+    @Override
+    protected <A extends Annotation> A getAnnotation(MethodParameter parameter, Class<A> annotType) {
+        return parameter.getParameterAnnotation(annotType);
+    }
+
+}

+ 204 - 0
hsweb-starter/src/main/java/org/hswebframework/web/starter/jackson/Jackson2Tokenizer.java

@@ -0,0 +1,204 @@
+/*
+ * Copyright 2002-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hswebframework.web.starter.jackson;
+
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.core.async.ByteArrayFeeder;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.deser.DefaultDeserializationContext;
+import com.fasterxml.jackson.databind.util.TokenBuffer;
+import org.springframework.core.codec.DecodingException;
+import org.springframework.core.io.buffer.DataBuffer;
+import org.springframework.core.io.buffer.DataBufferUtils;
+import reactor.core.Exceptions;
+import reactor.core.publisher.Flux;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Function;
+
+/**
+ * {@link Function} to transform a JSON stream of arbitrary size, byte array
+ * chunks into a {@code Flux<TokenBuffer>} where each token buffer is a
+ * well-formed JSON object.
+ *
+ * @author Arjen Poutsma
+ * @author Rossen Stoyanchev
+ * @author Juergen Hoeller
+ * @since 5.0
+ */
+final class Jackson2Tokenizer {
+
+	private final JsonParser parser;
+
+	private final DeserializationContext deserializationContext;
+
+	private final boolean tokenizeArrayElements;
+
+	private TokenBuffer tokenBuffer;
+
+	private int objectDepth;
+
+	private int arrayDepth;
+
+	// TODO: change to ByteBufferFeeder when supported by Jackson
+	// See https://github.com/FasterXML/jackson-core/issues/478
+	private final ByteArrayFeeder inputFeeder;
+
+
+	private Jackson2Tokenizer(
+			JsonParser parser, DeserializationContext deserializationContext, boolean tokenizeArrayElements) {
+
+		this.parser = parser;
+		this.deserializationContext = deserializationContext;
+		this.tokenizeArrayElements = tokenizeArrayElements;
+		this.tokenBuffer = new TokenBuffer(parser, deserializationContext);
+		this.inputFeeder = (ByteArrayFeeder) this.parser.getNonBlockingInputFeeder();
+	}
+
+
+	private List<TokenBuffer> tokenize(DataBuffer dataBuffer) {
+		byte[] bytes = new byte[dataBuffer.readableByteCount()];
+		dataBuffer.read(bytes);
+		DataBufferUtils.release(dataBuffer);
+
+		try {
+			this.inputFeeder.feedInput(bytes, 0, bytes.length);
+			return parseTokenBufferFlux();
+		}
+		catch (JsonProcessingException ex) {
+			throw new DecodingException("JSON decoding error: " + ex.getOriginalMessage(), ex);
+		}
+		catch (IOException ex) {
+			throw Exceptions.propagate(ex);
+		}
+	}
+
+	private Flux<TokenBuffer> endOfInput() {
+		return Flux.defer(() -> {
+			this.inputFeeder.endOfInput();
+			try {
+				return Flux.fromIterable(parseTokenBufferFlux());
+			}
+			catch (JsonProcessingException ex) {
+				throw new DecodingException("JSON decoding error: " + ex.getOriginalMessage(), ex);
+			}
+			catch (IOException ex) {
+				throw Exceptions.propagate(ex);
+			}
+		});
+	}
+
+	private List<TokenBuffer> parseTokenBufferFlux() throws IOException {
+		List<TokenBuffer> result = new ArrayList<>();
+
+		while (true) {
+			JsonToken token = this.parser.nextToken();
+			// SPR-16151: Smile data format uses null to separate documents
+			if (token == JsonToken.NOT_AVAILABLE ||
+					(token == null && (token = this.parser.nextToken()) == null)) {
+				break;
+			}
+			updateDepth(token);
+			if (!this.tokenizeArrayElements) {
+				processTokenNormal(token, result);
+			}
+			else {
+				processTokenArray(token, result);
+			}
+		}
+		return result;
+	}
+
+	private void updateDepth(JsonToken token) {
+		switch (token) {
+			case START_OBJECT:
+				this.objectDepth++;
+				break;
+			case END_OBJECT:
+				this.objectDepth--;
+				break;
+			case START_ARRAY:
+				this.arrayDepth++;
+				break;
+			case END_ARRAY:
+				this.arrayDepth--;
+				break;
+		}
+	}
+
+	private void processTokenNormal(JsonToken token, List<TokenBuffer> result) throws IOException {
+		this.tokenBuffer.copyCurrentEvent(this.parser);
+
+		if ((token.isStructEnd() || token.isScalarValue()) && this.objectDepth == 0 && this.arrayDepth == 0) {
+			result.add(this.tokenBuffer);
+			this.tokenBuffer = new TokenBuffer(this.parser, this.deserializationContext);
+		}
+
+	}
+
+	private void processTokenArray(JsonToken token, List<TokenBuffer> result) throws IOException {
+		if (!isTopLevelArrayToken(token)) {
+			this.tokenBuffer.copyCurrentEvent(this.parser);
+		}
+
+		if (this.objectDepth == 0 && (this.arrayDepth == 0 || this.arrayDepth == 1) &&
+				(token == JsonToken.END_OBJECT || token.isScalarValue())) {
+			result.add(this.tokenBuffer);
+			this.tokenBuffer = new TokenBuffer(this.parser, this.deserializationContext);
+		}
+	}
+
+	private boolean isTopLevelArrayToken(JsonToken token) {
+		return this.objectDepth == 0 && ((token == JsonToken.START_ARRAY && this.arrayDepth == 1) ||
+				(token == JsonToken.END_ARRAY && this.arrayDepth == 0));
+	}
+
+
+	/**
+	 * Tokenize the given {@code Flux<DataBuffer>} into {@code Flux<TokenBuffer>}.
+	 * @param dataBuffers the source data buffers
+	 * @param jsonFactory the factory to use
+	 * @param objectMapper the current mapper instance
+	 * @param tokenizeArrayElements if {@code true} and the "top level" JSON object is
+	 * an array, each element is returned individually immediately after it is received
+	 * @return the resulting token buffers
+	 */
+	public static Flux<TokenBuffer> tokenize(Flux<DataBuffer> dataBuffers, JsonFactory jsonFactory,
+			ObjectMapper objectMapper, boolean tokenizeArrayElements) {
+
+		try {
+			JsonParser parser = jsonFactory.createNonBlockingByteArrayParser();
+			DeserializationContext context = objectMapper.getDeserializationContext();
+			if (context instanceof DefaultDeserializationContext) {
+				context = ((DefaultDeserializationContext) context).createInstance(
+						objectMapper.getDeserializationConfig(), parser, objectMapper.getInjectableValues());
+			}
+			Jackson2Tokenizer tokenizer = new Jackson2Tokenizer(parser, context, tokenizeArrayElements);
+			return dataBuffers.concatMapIterable(tokenizer::tokenize).concatWith(tokenizer.endOfInput());
+		}
+		catch (IOException ex) {
+			return Flux.error(ex);
+		}
+	}
+
+}

+ 3 - 0
hsweb-starter/src/main/resources/META-INF/spring.factories

@@ -0,0 +1,3 @@
+# Auto Configure
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+org.hswebframework.web.starter.jackson.CustomCodecsAutoConfiguration

+ 0 - 26
hsweb-system/hsweb-system-authorization/hsweb-system-authorization-api/src/main/java/org/hswebframework/web/system/authorization/api/PermissionDimension.java

@@ -1,26 +0,0 @@
-package org.hswebframework.web.system.authorization.api;
-
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-import lombok.Setter;
-import org.hswebframework.web.authorization.DimensionType;
-
-import java.util.Map;
-
-@Getter
-@Setter
-@AllArgsConstructor(staticName = "of")
-@NoArgsConstructor
-public class PermissionDimension {
-
-    private String id;
-
-    private DimensionType dimensionType;
-
-    private Map<String, Object> properties;
-
-    public static PermissionDimension of(String id, DimensionType dimensionType) {
-        return of(id, dimensionType, null);
-    }
-}

+ 0 - 11
hsweb-system/hsweb-system-authorization/hsweb-system-authorization-api/src/main/java/org/hswebframework/web/system/authorization/api/PermissionDimensionProvider.java

@@ -1,11 +0,0 @@
-package org.hswebframework.web.system.authorization.api;
-
-import reactor.core.publisher.Flux;
-
-public interface PermissionDimensionProvider {
-
-    Flux<PermissionDimension> getDimensionByUserId(String userId);
-
-    Flux<String> getUserIdByDimensionId(String dimensionId);
-
-}

+ 5 - 3
hsweb-system/hsweb-system-authorization/hsweb-system-authorization-api/src/main/java/org/hswebframework/web/system/authorization/api/UserPermissionDimensionProvider.java

@@ -1,14 +1,16 @@
 package org.hswebframework.web.system.authorization.api;
 
 import org.hswebframework.web.authorization.DefaultDimensionType;
+import org.hswebframework.web.authorization.Dimension;
+import org.hswebframework.web.authorization.DimensionProvider;
 import reactor.core.publisher.Flux;
 
-public class UserPermissionDimensionProvider implements PermissionDimensionProvider {
+public class UserDimensionProvider implements DimensionProvider {
 
     @Override
-    public Flux<PermissionDimension> getDimensionByUserId(String userId) {
+    public Flux<Dimension> getDimensionByUserId(String userId) {
         return Flux.just(userId)
-                .map(id -> PermissionDimension.of(userId, DefaultDimensionType.user));
+                .map(id -> Dimension.of(userId, userId, DefaultDimensionType.user));
     }
 
     @Override

+ 11 - 1
hsweb-system/hsweb-system-authorization/hsweb-system-authorization-api/src/main/java/org/hswebframework/web/system/authorization/api/entity/AuthorizationSettingEntity.java

@@ -4,10 +4,14 @@ import lombok.Getter;
 import lombok.Setter;
 import org.hswebframework.ezorm.rdb.mapping.annotation.ColumnType;
 import org.hswebframework.ezorm.rdb.mapping.annotation.Comment;
+import org.hswebframework.ezorm.rdb.mapping.annotation.DefaultValue;
 import org.hswebframework.ezorm.rdb.mapping.annotation.JsonCodec;
 import org.hswebframework.web.api.crud.entity.Entity;
+import org.hswebframework.web.validator.CreateGroup;
 
 import javax.persistence.*;
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
 import java.sql.JDBCType;
 import java.util.List;
 import java.util.Set;
@@ -25,10 +29,12 @@ public class AuthorizationSettingEntity implements Entity {
 
     @Column(length = 32, nullable = false, updatable = false)
     @Comment("权限ID")
+    @NotBlank(message = "权限ID不能为空",groups = CreateGroup.class)
     private String permission;
 
-    @Column(length = 32, updatable = false)
+    @Column(length = 32, nullable = false,updatable = false)
     @Comment("维度")//如:user,role
+    @NotBlank(message = "维度不能为空",groups = CreateGroup.class)
     private String dimension;
 
     @Column(name = "dimension_name", length = 64)
@@ -37,6 +43,7 @@ public class AuthorizationSettingEntity implements Entity {
 
     @Column(name = "setting_target", length = 32, updatable = false)
     @Comment("维度目标")//具体的某个维度实例ID
+    @NotBlank(message = "维度目标不能为空",groups = CreateGroup.class)
     private String settingTarget;
 
     @Column(name = "setting_target_name", length = 64, updatable = false)
@@ -45,6 +52,7 @@ public class AuthorizationSettingEntity implements Entity {
 
     @Column(name = "state", nullable = false)
     @Comment("状态")
+    @NotNull(message = "状态不能为空",groups = CreateGroup.class)
     private Byte state;
 
     @Column
@@ -60,8 +68,10 @@ public class AuthorizationSettingEntity implements Entity {
     private List<DataAccessEntity> dataAccesses;
 
     @Column
+    @Comment("优先级")
     private Integer priority;
 
     @Column
+    @Comment("是否合并")
     private Boolean merge;
 }

+ 1 - 1
hsweb-system/hsweb-system-authorization/hsweb-system-authorization-api/src/main/java/org/hswebframework/web/system/authorization/api/entity/PermissionEntity.java

@@ -19,7 +19,7 @@ import java.util.Map;
 @Builder
 @NoArgsConstructor
 @AllArgsConstructor
-public class PermissionEntity  extends GenericEntity<String> {
+public class PermissionEntity extends GenericEntity<String> {
 
     @Column
     @Comment("权限名称")

+ 8 - 9
hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/java/org/hswebframework/web/system/authorization/defaults/configuration/AuthorizationServiceAutoConfiguration.java

@@ -3,11 +3,13 @@ package org.hswebframework.web.system.authorization.defaults.configuration;
 import org.hswebframework.ezorm.rdb.mapping.ReactiveRepository;
 import org.hswebframework.web.authorization.ReactiveAuthenticationInitializeService;
 import org.hswebframework.web.authorization.ReactiveAuthenticationManager;
-import org.hswebframework.web.system.authorization.api.UserPermissionDimensionProvider;
+import org.hswebframework.web.authorization.ReactiveAuthenticationManagerProvider;
+import org.hswebframework.web.system.authorization.api.UserDimensionProvider;
 import org.hswebframework.web.system.authorization.api.service.reactive.ReactiveUserService;
 import org.hswebframework.web.system.authorization.defaults.service.DefaultReactiveAuthenticationInitializeService;
 import org.hswebframework.web.system.authorization.defaults.service.DefaultReactiveAuthenticationManager;
 import org.hswebframework.web.system.authorization.defaults.service.DefaultReactiveUserService;
+import org.springframework.boot.autoconfigure.AutoConfigureBefore;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 import org.springframework.context.annotation.Bean;
@@ -17,9 +19,7 @@ import org.springframework.context.annotation.Configuration;
 @Configuration
 public class AuthorizationServiceAutoConfiguration {
 
-
-
-    @Configuration
+    @Configuration(proxyBeanMethods = false)
     static class ReactiveAuthorizationServiceAutoConfiguration{
         @ConditionalOnBean(ReactiveRepository.class)
         @Bean
@@ -27,10 +27,9 @@ public class AuthorizationServiceAutoConfiguration {
             return new DefaultReactiveUserService();
         }
 
-        @ConditionalOnMissingBean
-        @ConditionalOnBean(ReactiveUserService.class)
         @Bean
-        public ReactiveAuthenticationManager reactiveAuthenticationManager() {
+        @ConditionalOnBean(ReactiveUserService.class)
+        public ReactiveAuthenticationManagerProvider defaultReactiveAuthenticationManager() {
             return new DefaultReactiveAuthenticationManager();
         }
 
@@ -41,8 +40,8 @@ public class AuthorizationServiceAutoConfiguration {
         }
 
         @Bean
-        public UserPermissionDimensionProvider userPermissionDimensionProvider(){
-            return new UserPermissionDimensionProvider();
+        public UserDimensionProvider userPermissionDimensionProvider(){
+            return new UserDimensionProvider();
         }
     }
 

+ 1 - 1
hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/java/org/hswebframework/web/system/authorization/defaults/configuration/AuthorizationWebAutoConfiguration.java

@@ -10,7 +10,7 @@ import org.springframework.context.annotation.Configuration;
 public class AuthorizationWebAutoConfiguration {
 
 
-    @Configuration
+    @Configuration(proxyBeanMethods = false)
     @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
     public static class WebFluxAuthorizationConfiguration {
 

+ 32 - 0
hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/java/org/hswebframework/web/system/authorization/defaults/service/DefaultDimensionService.java

@@ -0,0 +1,32 @@
+package org.hswebframework.web.system.authorization.defaults.service;
+
+import org.hswebframework.web.crud.service.GenericReactiveCrudService;
+import org.hswebframework.web.crud.service.ReactiveTreeSortEntityService;
+import org.hswebframework.web.id.IDGenerator;
+import org.hswebframework.web.system.authorization.api.entity.DimensionEntity;
+import org.springframework.util.StringUtils;
+
+import java.util.List;
+
+public class DefaultDimensionService
+        extends GenericReactiveCrudService<DimensionEntity, String>
+        implements ReactiveTreeSortEntityService<DimensionEntity, String> {
+
+    @Override
+    public IDGenerator<String> getIDGenerator() {
+        return IDGenerator.MD5;
+    }
+
+    @Override
+    public void setChildren(DimensionEntity entity, List<DimensionEntity> children) {
+        entity.setChildren(children);
+    }
+
+    @Override
+    public List<DimensionEntity> getChildren(DimensionEntity entity) {
+        return entity.getChildren();
+    }
+
+
+
+}

+ 9 - 6
hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/java/org/hswebframework/web/system/authorization/defaults/service/DefaultReactiveAuthenticationInitializeService.java

@@ -3,16 +3,17 @@ package org.hswebframework.web.system.authorization.defaults.service;
 import org.apache.commons.collections.CollectionUtils;
 import org.hswebframework.ezorm.rdb.mapping.ReactiveRepository;
 import org.hswebframework.web.authorization.Authentication;
+import org.hswebframework.web.authorization.Dimension;
 import org.hswebframework.web.authorization.Permission;
 import org.hswebframework.web.authorization.ReactiveAuthenticationInitializeService;
 import org.hswebframework.web.authorization.access.DataAccessConfig;
+import org.hswebframework.web.authorization.access.DataAccessType;
 import org.hswebframework.web.authorization.builder.DataAccessConfigBuilderFactory;
 import org.hswebframework.web.authorization.simple.SimpleAuthentication;
 import org.hswebframework.web.authorization.simple.SimplePermission;
 import org.hswebframework.web.authorization.simple.SimpleUser;
 import org.hswebframework.web.authorization.simple.builder.SimpleDataAccessConfigBuilderFactory;
-import org.hswebframework.web.system.authorization.api.PermissionDimension;
-import org.hswebframework.web.system.authorization.api.PermissionDimensionProvider;
+import org.hswebframework.web.authorization.DimensionProvider;
 import org.hswebframework.web.system.authorization.api.entity.AuthorizationSettingEntity;
 import org.hswebframework.web.system.authorization.api.entity.PermissionEntity;
 import org.hswebframework.web.system.authorization.api.entity.UserEntity;
@@ -41,7 +42,7 @@ public class DefaultReactiveAuthenticationInitializeService
     private DataAccessConfigBuilderFactory builderFactory = new SimpleDataAccessConfigBuilderFactory();
 
     @Autowired(required = false)
-    private List<PermissionDimensionProvider> dimensionProviders = new ArrayList<>();
+    private List<DimensionProvider> dimensionProviders = new ArrayList<>();
 
     @Override
     public Mono<Authentication> initUserAuthorization(String userId) {
@@ -68,13 +69,14 @@ public class DefaultReactiveAuthenticationInitializeService
         return Flux.fromIterable(dimensionProviders)
                 .flatMap(provider -> provider.getDimensionByUserId(authentication.getUser().getId()))
                 .collectList()
+                .doOnNext(authentication::setDimensions)
                 .flatMap(allDimension -> Mono.zip(getAllPermission(),
                         settingRepository
                                 .createQuery()
                                 .where(AuthorizationSettingEntity::getState, 1)
                                 .in(AuthorizationSettingEntity::getDimension, allDimension
                                         .stream()
-                                        .map(PermissionDimension::getId)
+                                        .map(Dimension::getId)
                                         .collect(Collectors.toList()))
                                 .fetch()
                                 .collect(Collectors.groupingBy(AuthorizationSettingEntity::getPermission))
@@ -83,10 +85,11 @@ public class DefaultReactiveAuthenticationInitializeService
     }
 
     protected SimpleAuthentication handlePermission(SimpleAuthentication authentication,
-                                                    List<PermissionDimension> dimensionList,
+                                                    List<Dimension> dimensionList,
                                                     Map<String, PermissionEntity> permissions,
                                                     Map<String, List<AuthorizationSettingEntity>> settings) {
         List<Permission> permissionList = new ArrayList<>();
+
         for (PermissionEntity value : permissions.values()) {
             List<AuthorizationSettingEntity> permissionSettings = settings.get(value.getId());
             if (CollectionUtils.isEmpty(permissionSettings)) {
@@ -96,7 +99,7 @@ public class DefaultReactiveAuthenticationInitializeService
             SimplePermission permission = new SimplePermission();
             permission.setId(value.getId());
             permission.setName(value.getName());
-            Map<String, DataAccessConfig> configs = new HashMap<>();
+            Map<DataAccessType, DataAccessConfig> configs = new HashMap<>();
 
             for (AuthorizationSettingEntity permissionSetting : permissionSettings) {
 

+ 2 - 5
hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/java/org/hswebframework/web/system/authorization/defaults/service/DefaultReactiveAuthenticationManager.java

@@ -1,9 +1,6 @@
 package org.hswebframework.web.system.authorization.defaults.service;
 
-import org.hswebframework.web.authorization.Authentication;
-import org.hswebframework.web.authorization.AuthenticationRequest;
-import org.hswebframework.web.authorization.ReactiveAuthenticationInitializeService;
-import org.hswebframework.web.authorization.ReactiveAuthenticationManager;
+import org.hswebframework.web.authorization.*;
 import org.hswebframework.web.authorization.exception.AccessDenyException;
 import org.hswebframework.web.authorization.simple.PlainTextUsernamePasswordAuthenticationRequest;
 import org.hswebframework.web.system.authorization.api.entity.UserEntity;
@@ -13,7 +10,7 @@ import org.springframework.cache.CacheManager;
 import org.springframework.cache.support.SimpleValueWrapper;
 import reactor.core.publisher.Mono;
 
-public class DefaultReactiveAuthenticationManager implements ReactiveAuthenticationManager {
+public class DefaultReactiveAuthenticationManager implements ReactiveAuthenticationManagerProvider {
 
     @Autowired
     private ReactiveUserService reactiveUserService;

+ 7 - 8
hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/java/org/hswebframework/web/system/authorization/defaults/service/DefaultReactiveUserService.java

@@ -91,15 +91,14 @@ public class DefaultReactiveUserService extends GenericReactiveCrudService<UserE
 
     }
 
-
     @Override
-    @Transactional(readOnly = true, transactionManager = "connectionFactoryTransactionManager")
+    @Transactional(readOnly = true, transactionManager = TransactionManagers.r2dbcTransactionManager)
     public Mono<UserEntity> findById(String id) {
         return getRepository().findById(Mono.just(id));
     }
 
     @Override
-    @Transactional(readOnly = true, transactionManager = "connectionFactoryTransactionManager")
+    @Transactional(readOnly = true, transactionManager = TransactionManagers.r2dbcTransactionManager)
     public Mono<UserEntity> findByUsername(String username) {
         return Mono.justOrEmpty(username)
                 .flatMap(_name -> repository.createQuery()
@@ -108,7 +107,7 @@ public class DefaultReactiveUserService extends GenericReactiveCrudService<UserE
     }
 
     @Override
-    @Transactional(readOnly = true, transactionManager = "connectionFactoryTransactionManager")
+    @Transactional(readOnly = true, transactionManager = TransactionManagers.r2dbcTransactionManager)
     public Mono<UserEntity> findByUsernameAndPassword(String username, String plainPassword) {
         return Mono.justOrEmpty(username)
                 .flatMap(_name -> repository
@@ -120,7 +119,7 @@ public class DefaultReactiveUserService extends GenericReactiveCrudService<UserE
     }
 
     @Override
-    @Transactional(rollbackFor = Exception.class, transactionManager = "connectionFactoryTransactionManager")
+    @Transactional(rollbackFor = Exception.class, transactionManager = TransactionManagers.r2dbcTransactionManager)
     public Mono<Integer> changeState(Publisher<String> userId, byte state) {
         return Flux.from(userId)
                 .collectList()
@@ -133,7 +132,7 @@ public class DefaultReactiveUserService extends GenericReactiveCrudService<UserE
     }
 
     @Override
-    @Transactional(rollbackFor = Exception.class, transactionManager = "connectionFactoryTransactionManager")
+    @Transactional(rollbackFor = Exception.class, transactionManager =TransactionManagers.r2dbcTransactionManager)
     public Mono<Boolean> changePassword(String userId, String oldPassword, String newPassword) {
         return findById(userId)
                 .switchIfEmpty(Mono.error(NotFoundException::new))
@@ -147,7 +146,7 @@ public class DefaultReactiveUserService extends GenericReactiveCrudService<UserE
     }
 
     @Override
-    @Transactional(readOnly = true, transactionManager = "connectionFactoryTransactionManager")
+    @Transactional(readOnly = true, transactionManager = TransactionManagers.r2dbcTransactionManager)
     public Flux<UserEntity> findUser(QueryParam queryParam) {
         return repository
                 .createQuery()
@@ -156,7 +155,7 @@ public class DefaultReactiveUserService extends GenericReactiveCrudService<UserE
     }
 
     @Override
-    @Transactional(readOnly = true, transactionManager = "connectionFactoryTransactionManager")
+    @Transactional(readOnly = true, transactionManager = TransactionManagers.r2dbcTransactionManager)
     public Mono<Integer> countUser(QueryParam queryParam) {
         return repository
                 .createQuery()

+ 26 - 0
hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/java/org/hswebframework/web/system/authorization/defaults/webflux/WebFluxAuthorizationSettingController.java

@@ -0,0 +1,26 @@
+package org.hswebframework.web.system.authorization.defaults.webflux;
+
+
+import org.hswebframework.ezorm.rdb.mapping.ReactiveRepository;
+import org.hswebframework.web.authorization.annotation.Authorize;
+import org.hswebframework.web.authorization.annotation.Resource;
+import org.hswebframework.web.crud.web.reactive.ReactiveCrudController;
+import org.hswebframework.web.system.authorization.api.entity.AuthorizationSettingEntity;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/autz-setting")
+@Authorize
+@Resource(id = "autz-setting",name = "权限分配",group = "system")
+public class WebFluxAuthorizationSettingController implements ReactiveCrudController<AuthorizationSettingEntity, String> {
+
+    @Autowired
+    private ReactiveRepository<AuthorizationSettingEntity, String> reactiveRepository;
+
+    @Override
+    public ReactiveRepository<AuthorizationSettingEntity, String> getRepository() {
+        return reactiveRepository;
+    }
+}

+ 37 - 0
hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/java/org/hswebframework/web/system/authorization/defaults/webflux/WebFluxDimensionController.java

@@ -0,0 +1,37 @@
+package org.hswebframework.web.system.authorization.defaults.webflux;
+
+import org.hswebframework.web.api.crud.entity.QueryParamEntity;
+import org.hswebframework.web.authorization.annotation.Authorize;
+import org.hswebframework.web.authorization.annotation.QueryAction;
+import org.hswebframework.web.authorization.annotation.Resource;
+import org.hswebframework.web.crud.web.reactive.ReactiveServiceCrudController;
+import org.hswebframework.web.system.authorization.api.entity.DimensionEntity;
+import org.hswebframework.web.system.authorization.defaults.service.DefaultDimensionService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import reactor.core.publisher.Mono;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/dimension")
+@Authorize
+@Resource(id = "dimension",name = "权限维度管理",group = "system")
+public class WebFluxDimensionController implements ReactiveServiceCrudController<DimensionEntity, String> {
+
+    @Autowired
+    private DefaultDimensionService defaultDimensionService;
+
+    @GetMapping("/_query/tree")
+    @QueryAction
+    public Mono<List<DimensionEntity>> findAllTree(QueryParamEntity paramEntity) {
+        return defaultDimensionService.queryResultToTree(Mono.just(paramEntity));
+    }
+
+    @Override
+    public DefaultDimensionService getService() {
+        return defaultDimensionService;
+    }
+}

+ 9 - 6
hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/java/org/hswebframework/web/system/authorization/defaults/webflux/WebFluxPermissionController.java

@@ -6,6 +6,8 @@ import org.hswebframework.ezorm.rdb.mapping.defaults.SaveResult;
 import org.hswebframework.web.api.crud.entity.QueryParamEntity;
 import org.hswebframework.web.authorization.Permission;
 import org.hswebframework.web.authorization.annotation.Authorize;
+import org.hswebframework.web.authorization.annotation.QueryAction;
+import org.hswebframework.web.authorization.annotation.Resource;
 import org.hswebframework.web.exception.NotFoundException;
 import org.hswebframework.web.system.authorization.api.entity.PermissionEntity;
 import org.reactivestreams.Publisher;
@@ -18,14 +20,15 @@ import java.util.List;
 
 @RestController
 @RequestMapping("/permission")
-@Authorize(permission = "permission", description = "权限管理")
+@Authorize
+@Resource(id = "permission",name = "权限管理",group = "system")
 public class WebFluxPermissionController {
 
     @Autowired
     private ReactiveRepository<PermissionEntity, String> repository;
 
     @GetMapping
-    @Authorize(action = Permission.ACTION_QUERY)
+    @QueryAction
     public Flux<PermissionEntity> findAllPermission(QueryParamEntity paramEntity) {
         return repository.createQuery()
                 .setParam(paramEntity)
@@ -33,7 +36,7 @@ public class WebFluxPermissionController {
     }
 
     @GetMapping("/{id}")
-    @Authorize(action = Permission.ACTION_QUERY)
+    @QueryAction
     public Mono<PermissionEntity> getPermission(@PathVariable String id) {
         return Mono.just(id)
                 .as(repository::findById)
@@ -41,7 +44,7 @@ public class WebFluxPermissionController {
     }
 
     @GetMapping("/count")
-    @Authorize(action = Permission.ACTION_QUERY)
+    @QueryAction
     public Mono<Integer> countAllPermission(QueryParamEntity paramEntity) {
         return repository.createQuery()
                 .setParam(paramEntity)
@@ -50,13 +53,13 @@ public class WebFluxPermissionController {
     }
 
     @PatchMapping
-    @Authorize(action = Permission.ACTION_UPDATE)
+    @QueryAction
     public Mono<SaveResult> savePermission(@RequestBody Publisher<PermissionEntity> paramEntity) {
         return repository.save(paramEntity);
     }
 
     @PutMapping("/status/{status}")
-    @Authorize(action = Permission.ACTION_UPDATE)
+    @QueryAction
     public Mono<Integer> changePermissionState(@PathVariable Byte status, @RequestBody List<String> idList) {
 
         return Mono.just(idList)

+ 59 - 3
hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/java/org/hswebframework/web/system/authorization/defaults/webflux/WebFluxUserController.java

@@ -1,14 +1,70 @@
 package org.hswebframework.web.system.authorization.defaults.webflux;
 
+import lombok.Getter;
+import lombok.Setter;
+import org.hswebframework.web.authorization.Authentication;
+import org.hswebframework.web.authorization.User;
 import org.hswebframework.web.authorization.annotation.Authorize;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.hswebframework.web.authorization.annotation.Resource;
+import org.hswebframework.web.authorization.annotation.SaveAction;
+import org.hswebframework.web.authorization.exception.UnAuthorizedException;
+import org.hswebframework.web.crud.web.reactive.ReactiveServiceQueryController;
+import org.hswebframework.web.system.authorization.api.entity.UserEntity;
+import org.hswebframework.web.system.authorization.defaults.service.DefaultReactiveUserService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import reactor.core.publisher.Mono;
 
 @RestController
 @RequestMapping("/user")
 @Authorize
-public class WebFluxUserController {
+@Resource(id = "user", name = "系统用户", group = "system")
+public class WebFluxUserController implements ReactiveServiceQueryController<UserEntity, String> {
 
+    @Autowired
+    private DefaultReactiveUserService reactiveUserService;
 
+    @PatchMapping
+    @SaveAction
+    public Mono<Boolean> saveUser(@RequestBody Mono<UserEntity> user) {
+        return Authentication
+                .currentReactive()
+                .zipWith(user, ((u, e) -> {
+                    e.setCreateTimeNow();
+                    e.setCreatorId(u.getUser().getId());
+                    return e;
+                }))
+                .switchIfEmpty(user)
+                .as(reactiveUserService::saveUser);
+    }
 
+    @PutMapping("/{id:.+}/{state}")
+    @SaveAction
+    public Mono<Integer> changeState(@PathVariable String id, @PathVariable Byte state) {
+        return reactiveUserService.changeState(Mono.just(id), state);
+    }
+
+    @PutMapping("/passwd")
+    @Authorize(merge = false)
+    public Mono<Boolean> changePassword(@RequestBody ChangePasswordRequest request) {
+        return Authentication
+                .currentReactive()
+                .switchIfEmpty(Mono.error(UnAuthorizedException::new))
+                .map(Authentication::getUser)
+                .map(User::getId)
+                .flatMap(userId -> reactiveUserService.changePassword(userId, request.getOldPassword(), request.getNewPassword()));
+    }
+
+    @Override
+    public DefaultReactiveUserService getService() {
+        return reactiveUserService;
+    }
+
+    @Getter
+    @Setter
+    public static class ChangePasswordRequest {
+        private String oldPassword;
+
+        private String newPassword;
+    }
 }

+ 0 - 8
hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/test/java/org/hswebframework/web/system/authorization/defaults/service/reactive/ReactiveTestApplication.java

@@ -1,17 +1,9 @@
 package org.hswebframework.web.system.authorization.defaults.service.reactive;
 
 import org.hswebframework.web.crud.configuration.JdbcSqlExecutorConfiguration;
-import org.hswebframework.web.system.authorization.api.UserPermissionDimensionProvider;
-import org.hswebframework.web.system.authorization.defaults.service.DefaultReactiveAuthenticationInitializeService;
-import org.hswebframework.web.system.authorization.defaults.service.DefaultReactiveAuthenticationManager;
-import org.hswebframework.web.system.authorization.defaults.service.DefaultReactiveUserService;
 import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
-import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
-import org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration;
 import org.springframework.boot.autoconfigure.transaction.reactive.ReactiveTransactionAutoConfiguration;
-import org.springframework.context.annotation.Bean;
-import org.springframework.transaction.annotation.EnableTransactionManagement;
 
 @SpringBootApplication(exclude = {
          //TransactionAutoConfiguration.class,