Przeglądaj źródła

Merge branch '3.0'

zhouhao 7 lat temu
rodzic
commit
7d905d12b7
100 zmienionych plików z 2403 dodań i 618 usunięć
  1. 24 0
      FEATURES.md
  2. 16 7
      README.md
  3. BIN
      docs/hsweb3.png
  4. 3 2
      hsweb-authorization/hsweb-authorization-api/README.md
  5. 49 0
      hsweb-authorization/hsweb-authorization-api/custom-data-access.md
  6. 9 0
      hsweb-authorization/hsweb-authorization-api/pom.xml
  7. 4 6
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/Authentication.java
  8. 1 1
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/AuthenticationHolder.java
  9. 0 7
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/AuthenticationInitializeService.java
  10. 2 8
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/Permission.java
  11. 1 1
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/access/CustomDataAccess.java
  12. 34 7
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/access/DataAccessConfig.java
  13. 0 34
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/access/FieldAccessConfig.java
  14. 0 24
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/access/FieldAccessController.java
  15. 14 0
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/access/FieldFilterDataAccessConfig.java
  16. 23 0
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/access/FieldScopeDataAccessConfig.java
  17. 23 0
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/access/ScopeDataAccessConfig.java
  18. 4 0
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/annotation/Authorize.java
  19. 4 4
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/annotation/RequiresDataAccess.java
  20. 0 56
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/annotation/RequiresFieldAccess.java
  21. 55 0
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/builder/AuthenticationBuilder.java
  22. 10 0
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/builder/AuthenticationBuilderFactory.java
  23. 13 0
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/builder/DataAccessConfigBuilder.java
  24. 10 0
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/builder/DataAccessConfigBuilderFactory.java
  25. 5 3
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/listener/AuthorizationListenerDispatcher.java
  26. 2 2
      hsweb-system/hsweb-system-authorization/hsweb-system-authorization-service/hsweb-system-authorization-service-simple/src/main/java/org/hswebframework/web/service/authorization/simple/access/AbstractDataAccess.java
  27. 43 0
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/AuthorizationAutoConfiguration.java
  28. 88 0
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/SimpleAuthentication.java
  29. 5 5
      hsweb-system/hsweb-system-authorization/hsweb-system-authorization-service/hsweb-system-authorization-service-simple/src/main/java/org/hswebframework/web/service/authorization/simple/access/SimpleCustomDataAccess.java
  30. 45 0
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/SimpleFieldFilterDataAccessConfig.java
  31. 60 0
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/SimpleFiledScopeDataAccessConfig.java
  32. 18 0
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/SimpleOwnCreatedDataAccessConfig.java
  33. 55 0
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/SimplePermission.java
  34. 40 0
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/SimpleRole.java
  35. 4 4
      hsweb-system/hsweb-system-authorization/hsweb-system-authorization-service/hsweb-system-authorization-service-simple/src/main/java/org/hswebframework/web/service/authorization/simple/access/SimpleScriptDataAccess.java
  36. 50 0
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/SimpleUser.java
  37. 13 0
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/builder/DataAccessConfigConvert.java
  38. 119 0
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/builder/SimpleAuthenticationBuilder.java
  39. 24 0
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/builder/SimpleAuthenticationBuilderFactory.java
  40. 47 0
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/builder/SimpleDataAccessConfigBuilder.java
  41. 94 0
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/builder/SimpleDataAccessConfigBuilderFactory.java
  42. 3 0
      hsweb-authorization/hsweb-authorization-api/src/main/resources/META-INF/spring.factories
  43. 5 1
      hsweb-authorization/hsweb-authorization-oauth2/hsweb-authorization-oauth2-auth-server/src/main/java/org/hswebframework/web/authorization/oauth2/server/client/OAuth2Client.java
  44. 2 1
      hsweb-authorization/hsweb-authorization-oauth2/hsweb-authorization-oauth2-auth-server/src/main/java/org/hswebframework/web/authorization/oauth2/server/support/AbstractAuthorizationService.java
  45. 1 1
      hsweb-authorization/hsweb-authorization-oauth2/hsweb-authorization-oauth2-auth-server/src/main/java/org/hswebframework/web/authorization/oauth2/server/support/HttpTokenRequest.java
  46. 6 0
      hsweb-authorization/hsweb-authorization-oauth2/hsweb-authorization-oauth2-client/src/main/java/org/hswebframework/web/authorization/oauth2/client/AccessTokenInfo.java
  47. 5 7
      hsweb-authorization/hsweb-authorization-shiro/README.md
  48. 20 16
      hsweb-authorization/hsweb-authorization-shiro/src/main/java/org/hswebframework/web/authorization/shiro/ShiroAutoConfiguration.java
  49. 11 12
      hsweb-authorization/hsweb-authorization-shiro/src/main/java/org/hswebframework/web/authorization/shiro/boost/BoostAuthorizationAttributeSourceAdvisor.java
  50. 31 7
      hsweb-authorization/hsweb-authorization-shiro/src/main/java/org/hswebframework/web/authorization/shiro/boost/DataAccessAnnotationMethodInterceptor.java
  51. 4 4
      hsweb-authorization/hsweb-authorization-shiro/src/main/java/org/hswebframework/web/authorization/shiro/boost/DefaultDataAccessController.java
  52. 0 99
      hsweb-authorization/hsweb-authorization-shiro/src/main/java/org/hswebframework/web/authorization/shiro/boost/DefaultFieldAccessController.java
  53. 0 88
      hsweb-authorization/hsweb-authorization-shiro/src/main/java/org/hswebframework/web/authorization/shiro/boost/FieldAccessAnnotationMethodInterceptor.java
  54. 28 22
      hsweb-authorization/hsweb-authorization-shiro/src/main/java/org/hswebframework/web/authorization/shiro/boost/SimpleAuthorizeMethodInterceptor.java
  55. 3 3
      hsweb-authorization/hsweb-authorization-shiro/src/main/java/org/hswebframework/web/authorization/shiro/boost/handler/CustomDataAccessHandler.java
  56. 86 0
      hsweb-authorization/hsweb-authorization-shiro/src/main/java/org/hswebframework/web/authorization/shiro/boost/handler/FieldFilterDataAccessHandler.java
  57. 114 0
      hsweb-authorization/hsweb-authorization-shiro/src/main/java/org/hswebframework/web/authorization/shiro/boost/handler/FieldScopeDataAccessHandler.java
  58. 3 3
      hsweb-authorization/hsweb-authorization-shiro/src/main/java/org/hswebframework/web/authorization/shiro/boost/handler/OwnCreatedDataAccessHandler.java
  59. 1 1
      hsweb-authorization/hsweb-authorization-shiro/src/main/java/org/hswebframework/web/authorization/shiro/boost/handler/ScriptDataAccessHandler.java
  60. 11 0
      hsweb-boost/hsweb-boost-aop/src/main/java/org/hswebframework/web/boost/aop/context/MethodInterceptorHolder.java
  61. 21 0
      hsweb-boost/hsweb-boost-validator/hsweb-boost-validator-group/pom.xml
  62. 10 0
      hsweb-boost/hsweb-boost-validator/hsweb-boost-validator-group/src/main/java/org/hswebframework/web/validator/group/CreateGroup.java
  63. 10 0
      hsweb-boost/hsweb-boost-validator/hsweb-boost-validator-group/src/main/java/org/hswebframework/web/validator/group/UpdateGroup.java
  64. 1 0
      hsweb-boost/hsweb-boost-validator/pom.xml
  65. 1 1
      hsweb-commons/hsweb-commons-controller/pom.xml
  66. 2 0
      hsweb-commons/hsweb-commons-controller/src/main/java/org/hswebframework/web/controller/CreateController.java
  67. 0 26
      hsweb-commons/hsweb-commons-controller/src/main/java/org/hswebframework/web/controller/GenericEntityController.java
  68. 0 24
      hsweb-commons/hsweb-commons-controller/src/main/java/org/hswebframework/web/controller/SimpleGenericEntityController.java
  69. 13 8
      hsweb-commons/hsweb-commons-controller/src/main/java/org/hswebframework/web/controller/UpdateController.java
  70. 1 1
      hsweb-commons/hsweb-commons-dao/hsweb-commons-dao-api/src/main/java/org/hswebframework/web/dao/datasource/DatabaseType.java
  71. 53 0
      hsweb-commons/hsweb-commons-dao/hsweb-commons-dao-mybatis/src/main/java/org/hswebframework/web/dao/mybatis/DefaultJdbcExecutor.java
  72. 25 1
      hsweb-commons/hsweb-commons-dao/hsweb-commons-dao-mybatis/src/main/java/org/hswebframework/web/dao/mybatis/MyBatisAutoConfiguration.java
  73. 38 0
      hsweb-commons/hsweb-commons-dao/hsweb-commons-dao-mybatis/src/main/java/org/hswebframework/web/dao/mybatis/MybatisEntityFactory.java
  74. 13 0
      hsweb-commons/hsweb-commons-dao/hsweb-commons-dao-mybatis/src/main/java/org/hswebframework/web/dao/mybatis/MybatisMapperCustomer.java
  75. 36 8
      hsweb-commons/hsweb-commons-dao/hsweb-commons-dao-mybatis/src/main/java/org/hswebframework/web/dao/mybatis/MybatisProperties.java
  76. 10 2
      hsweb-commons/hsweb-commons-dao/hsweb-commons-dao-mybatis/src/main/java/org/hswebframework/web/dao/mybatis/builder/EasyOrmSqlBuilder.java
  77. 5 0
      hsweb-commons/hsweb-commons-entity/pom.xml
  78. 12 0
      hsweb-commons/hsweb-commons-entity/src/main/java/org/hswebframework/web/commons/entity/DataStatus.java
  79. 2 1
      hsweb-commons/hsweb-commons-entity/src/main/java/org/hswebframework/web/commons/entity/SortSupportEntity.java
  80. 80 33
      hsweb-commons/hsweb-commons-entity/src/main/java/org/hswebframework/web/commons/entity/TreeSupportEntity.java
  81. 71 32
      hsweb-commons/hsweb-commons-entity/src/main/java/org/hswebframework/web/commons/entity/factory/MapperEntityFactory.java
  82. 11 0
      hsweb-commons/hsweb-commons-entity/src/main/java/org/hswebframework/web/commons/entity/factory/PropertyCopier.java
  83. 19 3
      hsweb-commons/hsweb-commons-service/hsweb-commons-service-simple/src/main/java/org/hswebframework/web/service/AbstractService.java
  84. 1 1
      hsweb-commons/hsweb-commons-service/hsweb-commons-service-simple/src/main/java/org/hswebframework/web/service/AbstractTreeSortService.java
  85. 64 0
      hsweb-commons/hsweb-commons-service/hsweb-commons-service-simple/src/main/java/org/hswebframework/web/service/EnableCacheGernericEntityService.java
  86. 7 3
      hsweb-commons/hsweb-commons-service/hsweb-commons-service-simple/src/main/java/org/hswebframework/web/service/GenericEntityService.java
  87. 11 2
      hsweb-commons/hsweb-commons-utils/src/main/java/org/hswebframework/web/AopUtils.java
  88. 56 8
      hsweb-commons/hsweb-commons-utils/src/main/java/org/hswebframework/web/ThreadLocalUtils.java
  89. 9 9
      hsweb-concurrent/hsweb-concurrent-lock/hsweb-concurrent-lock-api/src/main/java/org/hswebframework/web/concurrent/lock/AbstactLocakManager.java
  90. 16 2
      hsweb-concurrent/hsweb-concurrent-lock/hsweb-concurrent-lock-api/src/main/java/org/hswebframework/web/concurrent/lock/LockManager.java
  91. 4 4
      hsweb-concurrent/hsweb-concurrent-lock/hsweb-concurrent-lock-api/src/main/java/org/hswebframework/web/concurrent/lock/SimpleLockManager.java
  92. 65 0
      hsweb-concurrent/hsweb-concurrent-lock/hsweb-concurrent-lock-api/src/main/java/org/hswebframework/web/concurrent/lock/annotation/Lock.java
  93. 64 0
      hsweb-concurrent/hsweb-concurrent-lock/hsweb-concurrent-lock-api/src/main/java/org/hswebframework/web/concurrent/lock/annotation/ReadLock.java
  94. 75 0
      hsweb-concurrent/hsweb-concurrent-lock/hsweb-concurrent-lock-api/src/main/java/org/hswebframework/web/concurrent/lock/annotation/WriteLock.java
  95. 9 10
      hsweb-concurrent/hsweb-concurrent-lock/hsweb-concurrent-lock-redis/src/main/java/org/hswebframework/web/concurrent/lock/redis/RedissonLockManager.java
  96. 16 0
      hsweb-concurrent/hsweb-concurrent-lock/hsweb-concurrent-lock-starter/pom.xml
  97. 99 0
      hsweb-concurrent/hsweb-concurrent-lock/hsweb-concurrent-lock-starter/src/main/java/org/hswebframework/web/concurrent/lock/starter/AopLockAdvisor.java
  98. 8 3
      hsweb-concurrent/hsweb-concurrent-lock/hsweb-concurrent-lock-starter/src/main/java/org/hswebframework/web/concurrent/lock/starter/LockFactoryAutoConfiguration.java
  99. 120 0
      hsweb-concurrent/hsweb-concurrent-lock/hsweb-concurrent-lock-starter/src/main/java/org/hswebframework/web/concurrent/lock/starter/LockProcessor.java
  100. 0 0
      hsweb-concurrent/hsweb-concurrent-lock/hsweb-concurrent-lock-starter/src/main/java/org/hswebframework/web/concurrent/lock/starter/RedisLockFactoryAutoConfiguration.java

+ 24 - 0
FEATURES.md

@@ -0,0 +1,24 @@
+# hsweb3.0
+hsweb3.0 是模块化的,每个功能可独立使用,可选择自己需要的功能依赖即可. 
+将来还将支持使用类似dubbo,spring-cloud的服务进行分布式管理.将不同的模块分布式运行.
+全部api都通过restful+json提供,前后分离. 目前暂未提供前端实现.
+
+## 核心功能
++ [x] 权限管理.支持基于角色的权限控制,支持行,列的数据级权限控制,可自由拓展控制方式. 
++ [x] 动态数据源.支持多数据源管理,灵活的切换方式,支持事务中切换数据源.
++ [x] 并发场景工具.分布式锁,计数器等实用功能
++ [x] 消息(mq)工具. 简单的消息封装,收发消息更方便.
++ [x] websocket.配合消息工具实现前端消息推送.
++ [x] OAuth2.0 服务和客户端,支持使用OAuth2.0进行单点登录
++ [x] 数据字典功能,支持自定义字典解析器,满足特定字典格式需求.
++ [x] 菜单管理.支持菜单分组,灵活配置菜单结构.
++ [ ] 动态脚本.在线编写脚本任务,维护系统更灵活.
++ [ ] 动态表单.在线设计表单(需前端支持),提供统一CRUD接口,随建随用.
++ [ ] 工作流引擎.配合动态表单,实现工作流灵活自定义.
++ [ ] ***组织架构.采用[地区-组织-部门-职位-人员]方式,支持灵活的数据权限控制.***
++ [ ] 定时调度.在线配置任务,使用动态脚本编写任务内容,维护更方便.
++ [ ] 文件管理. 文件上传下载统一接口,支持文件秒传.
++ [ ] 访问日志. 记录用户每次访问信息.
++ [ ] 历史记录. 记录数据修改记录.
++ [ ] 在线数据库管理. 在线维护数据库,执行sql等操作.
++ [ ] 代码生成器.可自定义模板,各种项目结构随心所欲.

+ 16 - 7
README.md

@@ -1,18 +1,27 @@
-## hsweb-framework 3.0
+## hsweb  3.0
+[![Join the chat at https://gitter.im/hs-web/hsweb-framework](https://badges.gitter.im/hs-web/hsweb-framework.svg)](https://gitter.im/hs-web/hsweb-framework?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+[![Build Status](https://travis-ci.org/hs-web/hsweb-framework.svg?branch=master)](https://travis-ci.org/hs-web/hsweb-framework)
+[![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg?style=flat-square)](https://www.apache.org/licenses/LICENSE-2.0.html)
+[![Insight.io](https://www.insight.io/repoBadge/github.com/hs-web/hsweb-framework)](https://insight.io/github.com/hs-web/hsweb-framework)
+
+[Features](FEATURES.md)
 
 ## 模块简介
 
 | 模块       | 说明          |   进度 |
 | ------------- |:-------------:| ----|
-|[hsweb-authorization](hsweb-authorization)|权限控制| 80%|
+|[hsweb-authorization](hsweb-authorization)|权限控制| 90%|
 |[hsweb-commons](hsweb-commons) |基础通用功能| 90%|
 |[hsweb-concurrent](hsweb-concurrent)|并发包,缓存,锁,计数器等| 80%|
 |[hsweb-core](hsweb-core)|框架核心| 90%|
-|[hsweb-datasource](hsweb-datasource)|数据源| 0%|
+|[hsweb-datasource](hsweb-datasource)|数据源| 90%|
 |[hsweb-examples](hsweb-examples)|例子,演示| 10%|
 |[hsweb-i18n](hsweb-i18n)|国际化| 0%|
-|[hsweb-logging](hsweb-logging)| 日志|  10%|
+|[hsweb-logging](hsweb-logging)| 日志|  80%|
 |[hsweb-message](hsweb-message)|mq,websocket...| 80%|
-|[hsweb-starter](hsweb-starter)|模块启动器| 80%|
-|[hsweb-system](hsweb-system)|**系统功能**| 20%|
-|[hsweb-tests](hsweb-tests)|测试| 80%|
+|[hsweb-starter](hsweb-starter)|模块启动器| 90%|
+|[hsweb-system](hsweb-system)|**系统功能**| 40%|
+|[hsweb-tests](hsweb-tests)|测试| 80%|
+
+
+![](./docs/hsweb3.png)

BIN
docs/hsweb3.png


+ 3 - 2
hsweb-authorization/hsweb-authorization-api/README.md

@@ -12,8 +12,9 @@ _点击名称,查看源代码注释获得使用说明_
 | ------------- |:-------------:| 
 | [`@Authorize`](src/main/java/org/hswebframework/web/authorization/annotation/Authorize.java)    | RBAC方式权限控制注解 | 
 | [`@RequiresExpression`](src/main/java/org/hswebframework/web/authorization/annotation/RequiresExpression.java)      | 表达式方式验证      | 
-| [`@RequiresDataAccess`](src/main/java/org/hswebframework/web/authorization/annotation/RequiresDataAccess.java)      | 行级权限控制      | 
-| [`@RequiresFieldAccess`](src/main/java/org/hswebframework/web/authorization/annotation/RequiresFieldAccess.java)      | 列级权限控制      | 
+| [`@RequiresDataAccess`](src/main/java/org/hswebframework/web/authorization/annotation/RequiresDataAccess.java)      | 数据权限控制      | 
+
+[自定义数据权限控制](custom-data-access.md)
 
 ### 常用类
 _点击名称,查看源代码注释获得使用说明_

+ 49 - 0
hsweb-authorization/hsweb-authorization-api/custom-data-access.md

@@ -0,0 +1,49 @@
+# 自定义拓展数据权限控制
+
+1. 编写配置转换器,将在前端配置的内容转换为api需要的配置信息
+
+实现 ``DataAccessConfigConvert``接口
+```java
+@org.springframework.stereotype.Component
+public class MyDataAccessConfigConvert implements DataAccessConfigConvert {
+
+    @Override
+    public boolean isSupport(String type, String action, String config) {
+        return "custom_type".equals(type);
+    }
+
+    @Override
+    public DataAccessConfig convert(String type, String action, String config) {
+        MyDataAccessConfig accessConfig = JSON.parseObject(config, MyDataAccessConfig.class);
+        accessConfig.setAction(action);
+        accessConfig.setType(type);
+        return accessConfig;
+    }
+}
+```
+
+
+2. 实现 ``DataAccessHandler``接口
+```java
+@org.springframework.stereotype.Component //提供给Spring才会生效
+public class MyDataAccessHandler implements org.hswebframework.web.authorization.access.DataAccessHandler{
+    
+        @Override
+        public boolean isSupport(DataAccessConfig access) {
+            //DataAccessConfig 在用户登录的时候,初始化
+            //DataAccessConfig 由
+            //支持的配置类型
+            return "custom_type".equals(access.getType());
+        }
+    
+        //处理请求,返回true表示授权通过
+        @Override
+        public boolean handle(DataAccessConfig access, MethodInterceptorParamContext context) {
+            //被拦截的方法参数
+           Map<String,Object> param= context.getParams();
+           // 判断逻辑
+           //...
+            return true;
+        }
+}
+```

+ 9 - 0
hsweb-authorization/hsweb-authorization-api/pom.xml

@@ -16,6 +16,15 @@
             <artifactId>hsweb-boost-aop</artifactId>
             <version>${project.version}</version>
         </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter</artifactId>
+            <optional>true</optional>
+        </dependency>
         <dependency>
             <groupId>javax.servlet</groupId>
             <artifactId>servlet-api</artifactId>

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

@@ -78,12 +78,11 @@ public interface Authentication extends Serializable {
      * @param id 角色id
      * @return 角色信息
      */
-    default Role getRole(String id) {
+    default Optional<Role> getRole(String id) {
         if (null == id) return null;
         return getRoles().stream()
                 .filter(role -> role.getId().equals(id))
-                .findAny()
-                .orElse(null);
+                .findAny();
     }
 
     /**
@@ -92,12 +91,11 @@ public interface Authentication extends Serializable {
      * @param id 权限id
      * @return 权限信息
      */
-    default Permission getPermission(String id) {
+    default Optional<Permission> getPermission(String id) {
         if (null == id) return null;
         return getPermissions().parallelStream()
                 .filter(permission -> permission.getId().equals(id))
-                .findAny()
-                .orElse(null);
+                .findAny();
     }
 
     /**

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

@@ -96,7 +96,7 @@ public final class AuthenticationHolder {
         }
     }
 
-    public static void setCureentUserId(String id) {
+    public static void setCurrentUserId(String id) {
         ThreadLocalUtils.put(AuthenticationHolder.CURRENT_USER_ID_KEY, id);
     }
 }

+ 0 - 7
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/AuthenticationInitializeService.java

@@ -33,11 +33,4 @@ public interface AuthenticationInitializeService {
      */
     Authentication initUserAuthorization(String userId);
 
-    /**
-     * 将指定的用户初始化为超级管理员权限
-     *
-     * @param userId 用户ID
-     * @return 权限信息
-     */
-    Authentication initAdminAuthorization(String userId);
 }

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

@@ -18,7 +18,6 @@
 package org.hswebframework.web.authorization;
 
 import org.hswebframework.web.authorization.access.DataAccessConfig;
-import org.hswebframework.web.authorization.access.FieldAccessConfig;
 
 import java.io.Serializable;
 import java.util.Set;
@@ -48,6 +47,7 @@ public interface Permission extends Serializable {
      * 更新
      */
     String ACTION_UPDATE = "update";
+
     /**
      * 删除
      */
@@ -81,15 +81,9 @@ public interface Permission extends Serializable {
      */
     Set<String> getActions();
 
-    /**
-     * @return 用户对此权限持有的字段权限信息, 用于字段级别的控制
-     * @see FieldAccessConfig
-     */
-    Set<FieldAccessConfig> getFieldAccesses();
-
     /**
      * @return 用户对此权限持有的数据权限信息, 用于数据级别的控制
      * @see DataAccessConfig
      */
-    Set<DataAccessConfig> getDataAccessConfigs();
+    Set<DataAccessConfig> getDataAccesses();
 }

+ 1 - 1
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/access/CustomDataAccess.java

@@ -6,7 +6,7 @@ package org.hswebframework.web.authorization.access;
  * @author zhouhao
  * @see DefaultType#CUSTOM
  */
-public interface CustomDataAccess extends DataAccessConfig {
+public interface CustomDataAccessConfig extends DataAccessConfig {
 
     /**
      * @return 自定义的控制器

+ 34 - 7
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/access/DataAccessConfig.java

@@ -28,7 +28,7 @@ import java.io.Serializable;
  * 具体的控制逻辑由控制器{@link DataAccessController}实现
  *
  * @author zhouhao
- * @see org.hswebframework.web.authorization.access.CustomDataAccess
+ * @see CustomDataAccessConfig
  * @see OwnCreatedDataAccessConfig
  * @see ScriptDataAccessConfig
  */
@@ -55,14 +55,41 @@ public interface DataAccessConfig extends Serializable {
     String getType();
 
     /**
-     * 内置3中控制方式
+     * 内置控制方式
      */
     interface DefaultType {
-        //自己创建的数据
+        /**
+         * 自己创建的数据
+         *
+         * @see OwnCreatedDataAccessConfig#getType()
+         */
         String OWN_CREATED = "OWN_CREATED";
-        //脚本
-        String SCRIPT      = "SCRIPT";
-        //自定义控制器
-        String CUSTOM      = "CUSTOM";
+        /**
+         * 字段值范围
+         *
+         * @see FieldScopeDataAccessConfig#getType()
+         */
+        String FIELD_SCOPE = "FIELD_SCOPE";
+
+        /**
+         * 字段过滤,黑名单
+         *
+         * @see FieldFilterDataAccessConfig#getType()
+         */
+        String DENY_FIELDS = "DENY_FIELDS";
+
+        /**
+         * 自定义脚本方式
+         *
+         * @see ScriptDataAccessConfig#getType()
+         */
+        String SCRIPT = "SCRIPT";
+
+        /**
+         * 自定义控制器
+         *
+         * @see CustomDataAccessConfig#getType()
+         */
+        String CUSTOM = "CUSTOM";
     }
 }

+ 0 - 34
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/access/FieldAccessConfig.java

@@ -1,34 +0,0 @@
-package org.hswebframework.web.authorization.access;
-
-import java.io.Serializable;
-import java.util.Set;
-
-/**
- * 字段级别权限控制配置,表示此用户不能对字段{@link this#getField()} 执行 {@link this#getActions()}操作
- *
- * @author zhouhao
- * @see FieldAccessController
- */
-public interface FieldAccessConfig extends Serializable {
-
-    /**
-     * @return 要控制的字段名称, 字段名称支持嵌套如: user.info.name
-     */
-    String getField();
-
-    /**
-     * @return 对此字段的操作权限
-     * @see org.hswebframework.web.authorization.Permission#ACTION_QUERY
-     * @see org.hswebframework.web.authorization.Permission#ACTION_UPDATE
-     */
-    Set<String> getActions();
-
-    default Type getType() {
-        return Type.DENY;
-    }
-
-    enum Type {
-        //目前之支持 deny
-        DENY
-    }
-}

+ 0 - 24
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/access/FieldAccessController.java

@@ -1,24 +0,0 @@
-package org.hswebframework.web.authorization.access;
-
-import org.hswebframework.web.authorization.Permission;
-import org.hswebframework.web.boost.aop.context.MethodInterceptorParamContext;
-
-import java.util.Set;
-
-/**
- * 字段级权限控制器,用于控制对字段的操作权限。如:不同角色,可操作的字段不同等
- *
- * @author zhouhao
- */
-public interface FieldAccessController {
-
-    /**
-     * 执行权限验证。根据当前被拦截的操作类型,以及此类型可操作的字段集合进行权限验证
-     *
-     * @param action   当前操作的类型 {@link Permission#getActions()}
-     * @param accesses 不可操作的字段
-     * @param params   参数上下文
-     * @return 验证是否通过
-     */
-    boolean doAccess(String action, Set<FieldAccessConfig> accesses, MethodInterceptorParamContext params);
-}

+ 14 - 0
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/access/FieldFilterDataAccessConfig.java

@@ -0,0 +1,14 @@
+package org.hswebframework.web.authorization.access;
+
+import java.util.Set;
+
+/**
+ * 对字段进行过滤操作配置,实现字段级别的权限控制
+ *
+ * @author zhouhao
+ * @see DataAccessConfig
+ * @see org.hswebframework.web.authorization.simple.SimpleFieldFilterDataAccessConfig
+ */
+public interface FieldFilterDataAccessConfig extends DataAccessConfig {
+    Set<String> getFields();
+}

+ 23 - 0
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/access/FieldScopeDataAccessConfig.java

@@ -0,0 +1,23 @@
+package org.hswebframework.web.authorization.access;
+
+
+import static org.hswebframework.web.authorization.access.DataAccessConfig.DefaultType.FIELD_SCOPE;
+
+/**
+ * 范围数据权限控制配置,控制某个字段的值在范围内
+ *
+ * @author zhouhao
+ * @see ScopeDataAccessConfig
+ * @since 3.0
+ */
+public interface FieldScopeDataAccessConfig extends ScopeDataAccessConfig {
+    /**
+     * @return 字段信息
+     */
+    String getField();
+
+    @Override
+    default String getType() {
+        return FIELD_SCOPE;
+    }
+}

+ 23 - 0
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/access/ScopeDataAccessConfig.java

@@ -0,0 +1,23 @@
+package org.hswebframework.web.authorization.access;
+
+import java.util.Set;
+
+/**
+ * 范围数据权限控制配置
+ *
+ * @author zhouhao
+ * @see DataAccessConfig
+ * @since 3.0
+ */
+public interface ScopeDataAccessConfig extends DataAccessConfig {
+
+    /**
+     * @return 范围类型
+     */
+    String getScopeType();
+
+    /**
+     * @return 自定义的控制范围
+     */
+    Set<Object> getScope();
+}

+ 4 - 0
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/annotation/Authorize.java

@@ -89,4 +89,8 @@ public @interface Authorize {
      */
     Logical logical() default Logical.DEFAULT;
 
+    /**
+     * @return 是否忽略, 忽略后将不进行权限控制
+     */
+    boolean ignore() default false;
 }

+ 4 - 4
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/annotation/RequiresDataAccess.java

@@ -39,13 +39,13 @@ import java.lang.annotation.*;
 public @interface RequiresDataAccess {
 
     /**
-     * @return permission id
+     * @return permission id ,如果为空将继承 {@link Authorize#permission()}
      * @see Permission#getId()
      */
-    String permission();
+    String permission() default "";
 
     /**
-     * @return action array
+     * @return action array ,如果为空将继承 {@link Authorize#action()}
      * @see DataAccessConfig#getAction()
      */
     String[] action() default {};
@@ -53,7 +53,7 @@ public @interface RequiresDataAccess {
     /**
      * @return logical
      */
-    Logical logical() default Logical.OR;
+    Logical logical() default Logical.AND;
 
     /**
      * @return 自定义控制器bean名称

+ 0 - 56
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/annotation/RequiresFieldAccess.java

@@ -1,56 +0,0 @@
-/*
- * Copyright 2016 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.annotation;
-
-import org.hswebframework.web.authorization.Permission;
-import org.hswebframework.web.authorization.access.FieldAccessConfig;
-
-import java.lang.annotation.*;
-
-/**
- * 字段级权限控制注解,用于进行需要字段级别权限控制的声明.
- * <p>
- * 此注解仅用于声明此方法需要进行字段级权限控制,具体权限控制方式由控制器实{@link org.hswebframework.web.authorization.access.FieldAccessController}现
- * </p>
- *
- * @author zhouhao
- * @see org.hswebframework.web.authorization.access.FieldAccessController
- * @since 3.0
- */
-@Target({ElementType.TYPE, ElementType.METHOD})
-@Retention(RetentionPolicy.RUNTIME)
-@Documented
-public @interface RequiresFieldAccess {
-
-    /**
-     * @return permission id
-     * @see Permission#getId()
-     */
-    String permission();
-
-    /**
-     * @return action
-     * @see FieldAccessConfig#getActions()
-     */
-    String action();
-
-    Logical logical() default Logical.OR;
-
-    String paramName() default "";
-
-}

+ 55 - 0
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/builder/AuthenticationBuilder.java

@@ -0,0 +1,55 @@
+/*
+ * Copyright 2016 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.builder;
+
+import org.hswebframework.web.authorization.Authentication;
+import org.hswebframework.web.authorization.Permission;
+import org.hswebframework.web.authorization.Role;
+import org.hswebframework.web.authorization.User;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+
+public interface AuthenticationBuilder extends Serializable {
+
+    AuthenticationBuilder user(User user);
+
+    AuthenticationBuilder user(String user);
+
+    AuthenticationBuilder user(Map<String, String> user);
+
+
+    AuthenticationBuilder role(List<Role> role);
+
+    AuthenticationBuilder role(String role);
+
+
+    AuthenticationBuilder permission(List<Permission> permission);
+
+    AuthenticationBuilder permission(String permission);
+
+    AuthenticationBuilder attributes(String attributes);
+
+    AuthenticationBuilder attributes(Map<String, Serializable> permission);
+
+    AuthenticationBuilder json(String json);
+
+    Authentication build();
+
+}

+ 10 - 0
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/builder/AuthenticationBuilderFactory.java

@@ -0,0 +1,10 @@
+package org.hswebframework.web.authorization.builder;
+
+/**
+ * TODO 完成注释
+ *
+ * @author zhouhao
+ */
+public interface AuthenticationBuilderFactory {
+    AuthenticationBuilder create();
+}

+ 13 - 0
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/builder/DataAccessConfigBuilder.java

@@ -0,0 +1,13 @@
+package org.hswebframework.web.authorization.builder;
+
+import org.hswebframework.web.authorization.access.DataAccessConfig;
+
+/**
+ *
+ * @author zhouhao
+ */
+public interface DataAccessConfigBuilder {
+    DataAccessConfigBuilder fromJson(String json);
+
+    DataAccessConfig build();
+}

+ 10 - 0
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/builder/DataAccessConfigBuilderFactory.java

@@ -0,0 +1,10 @@
+package org.hswebframework.web.authorization.builder;
+
+/**
+ * TODO 完成注释
+ *
+ * @author zhouhao
+ */
+public interface DataAccessConfigBuilderFactory {
+    DataAccessConfigBuilder create();
+}

+ 5 - 3
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/listener/AuthorizationListenerDispatcher.java

@@ -35,15 +35,17 @@ public class AuthorizationListenerDispatcher {
     }
 
     @SuppressWarnings("unchecked")
-    public <E extends AuthorizationEvent> void doEvent(Class<E> eventType, E event) {
+    public <E extends AuthorizationEvent> int doEvent(Class<E> eventType, E event) {
         List<AuthorizationListener<E>> store = (List) listenerStore.get(eventType);
         if (null != store) {
             store.forEach(listener -> listener.on(event));
+            return store.size();
         }
+        return 0;
     }
 
     @SuppressWarnings("unchecked")
-    public <E extends AuthorizationEvent> void doEvent(E event) {
-        doEvent((Class<E>) event.getClass(), event);
+    public <E extends AuthorizationEvent> int doEvent(E event) {
+        return doEvent((Class<E>) event.getClass(), event);
     }
 }

+ 2 - 2
hsweb-system/hsweb-system-authorization/hsweb-system-authorization-service/hsweb-system-authorization-service-simple/src/main/java/org/hswebframework/web/service/authorization/simple/access/AbstractDataAccess.java

@@ -1,4 +1,4 @@
-package org.hswebframework.web.service.authorization.simple.access;
+package org.hswebframework.web.authorization.simple;
 
 import org.hswebframework.web.authorization.access.DataAccessConfig;
 
@@ -7,7 +7,7 @@ import org.hswebframework.web.authorization.access.DataAccessConfig;
  *
  * @author zhouhao
  */
-public abstract class AbstractDataAccess implements DataAccessConfig {
+public abstract class AbstractDataAccessConfig implements DataAccessConfig {
 
     private String action;
 

+ 43 - 0
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/AuthorizationAutoConfiguration.java

@@ -0,0 +1,43 @@
+package org.hswebframework.web.authorization.simple;
+
+import org.hswebframework.web.authorization.builder.AuthenticationBuilderFactory;
+import org.hswebframework.web.authorization.builder.DataAccessConfigBuilderFactory;
+import org.hswebframework.web.authorization.simple.builder.DataAccessConfigConvert;
+import org.hswebframework.web.authorization.simple.builder.SimpleAuthenticationBuilderFactory;
+import org.hswebframework.web.authorization.simple.builder.SimpleDataAccessConfigBuilderFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+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.Configuration;
+
+import java.util.List;
+
+/**
+ * TODO 完成注释
+ *
+ * @author zhouhao
+ */
+@Configuration
+public class AuthorizationAutoConfiguration {
+
+    @Autowired(required = false)
+    private List<DataAccessConfigConvert> dataAccessConfigConverts;
+
+    @Bean
+    @ConditionalOnMissingBean(DataAccessConfigBuilderFactory.class)
+    @ConfigurationProperties(prefix = "hsweb.authorization.data-access", ignoreInvalidFields = true)
+    public SimpleDataAccessConfigBuilderFactory dataAccessConfigBuilderFactory() {
+        SimpleDataAccessConfigBuilderFactory factory = new SimpleDataAccessConfigBuilderFactory();
+        if (null != dataAccessConfigConverts) {
+            dataAccessConfigConverts.forEach(factory::addConvert);
+        }
+        return factory;
+    }
+
+    @Bean
+    @ConditionalOnMissingBean(AuthenticationBuilderFactory.class)
+    public AuthenticationBuilderFactory authenticationBuilderFactory(DataAccessConfigBuilderFactory dataAccessConfigBuilderFactory) {
+        return new SimpleAuthenticationBuilderFactory(dataAccessConfigBuilderFactory);
+    }
+}

+ 88 - 0
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/SimpleAuthentication.java

@@ -0,0 +1,88 @@
+/*
+ * Copyright 2016 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.simple;
+
+import org.hswebframework.web.authorization.*;
+
+import java.io.Serializable;
+import java.util.*;
+
+public class SimpleAuthentication implements Authentication {
+
+    private User user;
+
+    private List<Role> roles;
+
+    private List<Permission> permissions;
+
+    private Map<String, Serializable> attributes = new HashMap<>();
+
+    @Override
+    public User getUser() {
+        return user;
+    }
+
+    public void setUser(User user) {
+        this.user = user;
+    }
+
+    public void setRoles(List<Role> roles) {
+        this.roles = roles;
+    }
+
+    public void setPermissions(List<Permission> permissions) {
+        this.permissions = permissions;
+    }
+
+    @Override
+    public List<Role> getRoles() {
+        return new ArrayList<>(roles);
+    }
+
+    @Override
+    public List<Permission> getPermissions() {
+        return new ArrayList<>(permissions);
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public <T extends Serializable> Optional<T> getAttribute(String name) {
+        return Optional.ofNullable((T) attributes.get(name));
+    }
+
+    @Override
+    public void setAttribute(String name, Serializable object) {
+        attributes.put(name, object);
+    }
+
+    @Override
+    public void setAttributes(Map<String, Serializable> attributes) {
+        this.attributes.putAll(attributes);
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public <T extends Serializable> T removeAttributes(String name) {
+        return (T) attributes.remove(name);
+    }
+
+    @Override
+    public Map<String, Serializable> getAttributes() {
+        return attributes;
+    }
+}

+ 5 - 5
hsweb-system/hsweb-system-authorization/hsweb-system-authorization-service/hsweb-system-authorization-service-simple/src/main/java/org/hswebframework/web/service/authorization/simple/access/SimpleCustomDataAccess.java

@@ -1,21 +1,21 @@
-package org.hswebframework.web.service.authorization.simple.access;
+package org.hswebframework.web.authorization.simple;
 
-import org.hswebframework.web.authorization.access.CustomDataAccess;
+import org.hswebframework.web.authorization.access.CustomDataAccessConfig;
 import org.hswebframework.web.authorization.access.DataAccessController;
 
 /**
  * @author zhouhao
  */
-public class SimpleCustomDataAccess extends AbstractDataAccess implements CustomDataAccess {
+public class SimpleCustomDataAccessConfigConfig extends AbstractDataAccessConfig implements CustomDataAccessConfig {
 
     private String classOrBeanName;
 
     private transient DataAccessController instance;
 
-    public SimpleCustomDataAccess() {
+    public SimpleCustomDataAccessConfigConfig() {
     }
 
-    public SimpleCustomDataAccess(String classOrBeanName) {
+    public SimpleCustomDataAccessConfigConfig(String classOrBeanName) {
         this.classOrBeanName = classOrBeanName;
     }
 

+ 45 - 0
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/SimpleFieldFilterDataAccessConfig.java

@@ -0,0 +1,45 @@
+package org.hswebframework.web.authorization.simple;
+
+import org.hswebframework.web.authorization.access.FieldFilterDataAccessConfig;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * 默认配置实现
+ *
+ * @author zhouhao
+ * @see FieldFilterDataAccessConfig
+ * @since 3.0
+ */
+public class SimpleFieldFilterDataAccessConfig extends AbstractDataAccessConfig implements FieldFilterDataAccessConfig {
+    private Set<String> fields;
+
+    private String type;
+
+    public SimpleFieldFilterDataAccessConfig() {
+    }
+
+    public SimpleFieldFilterDataAccessConfig(String... fields) {
+        this.fields = new HashSet<>(Arrays.asList(fields));
+    }
+
+    @Override
+    public Set<String> getFields() {
+        return fields;
+    }
+
+    public void setFields(Set<String> fields) {
+        this.fields = fields;
+    }
+
+    @Override
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+}

+ 60 - 0
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/SimpleFiledScopeDataAccessConfig.java

@@ -0,0 +1,60 @@
+package org.hswebframework.web.authorization.simple;
+
+import org.hswebframework.web.authorization.access.FieldScopeDataAccessConfig;
+
+import java.util.Set;
+
+/**
+ * TODO 完成注释
+ *
+ * @author zhouhao
+ */
+public class SimpleFiledScopeDataAccessConfig extends AbstractDataAccessConfig implements FieldScopeDataAccessConfig {
+
+    private String scopeType;
+
+    private Set<Object> scope;
+
+    private String field;
+
+    public SimpleFiledScopeDataAccessConfig() {
+    }
+
+    public SimpleFiledScopeDataAccessConfig(String field, Set<Object> scope) {
+        this.scope = scope;
+        this.field = field;
+    }
+
+    public SimpleFiledScopeDataAccessConfig(String field, String scopeType) {
+        this.scopeType = scopeType;
+        this.field = field;
+    }
+
+    @Override
+    public String getScopeType() {
+        return scopeType;
+    }
+
+    public void setScopeType(String scopeType) {
+        this.scopeType = scopeType;
+    }
+
+    @Override
+    public Set<Object> getScope() {
+        return scope;
+    }
+
+    public void setScope(Set<Object> scope) {
+        this.scope = scope;
+    }
+
+    @Override
+    public String getField() {
+        return field;
+    }
+
+    public void setField(String field) {
+        this.field = field;
+    }
+
+}

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

@@ -0,0 +1,18 @@
+package org.hswebframework.web.authorization.simple;
+
+import org.hswebframework.web.authorization.access.OwnCreatedDataAccessConfig;
+
+/**
+ * TODO 完成注释
+ *
+ * @author zhouhao
+ */
+public class SimpleOwnCreatedDataAccessConfig extends AbstractDataAccessConfig implements OwnCreatedDataAccessConfig {
+
+    public SimpleOwnCreatedDataAccessConfig() {
+    }
+
+    public SimpleOwnCreatedDataAccessConfig(String action) {
+        setAction(action);
+    }
+}

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

@@ -0,0 +1,55 @@
+package org.hswebframework.web.authorization.simple;
+
+import org.hswebframework.web.authorization.Permission;
+import org.hswebframework.web.authorization.access.DataAccessConfig;
+
+import java.util.Set;
+
+/**
+ * TODO 完成注释
+ *
+ * @author zhouhao
+ */
+public class SimplePermission implements Permission {
+
+    private String id;
+
+    private Set<String> actions;
+
+    private Set<DataAccessConfig> dataAccesses;
+
+    public SimplePermission() {
+    }
+
+    public SimplePermission(String id, Set<String> actions) {
+        this.id = id;
+        this.actions = actions;
+    }
+
+    @Override
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    @Override
+    public Set<String> getActions() {
+        return actions;
+    }
+
+    public void setActions(Set<String> actions) {
+        this.actions = actions;
+    }
+
+    @Override
+    public Set<DataAccessConfig> getDataAccesses() {
+        return dataAccesses;
+    }
+
+    public void setDataAccesses(Set<DataAccessConfig> dataAccesses) {
+        this.dataAccesses = dataAccesses;
+    }
+}

+ 40 - 0
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/SimpleRole.java

@@ -0,0 +1,40 @@
+package org.hswebframework.web.authorization.simple;
+
+import org.hswebframework.web.authorization.Role;
+
+/**
+ * TODO 完成注释
+ *
+ * @author zhouhao
+ */
+public class SimpleRole implements Role {
+    private String id;
+
+    private String name;
+
+    public SimpleRole() {
+    }
+
+    public SimpleRole(String id, String name) {
+        this.id = id;
+        this.name = name;
+    }
+
+    @Override
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+}

+ 4 - 4
hsweb-system/hsweb-system-authorization/hsweb-system-authorization-service/hsweb-system-authorization-service-simple/src/main/java/org/hswebframework/web/service/authorization/simple/access/SimpleScriptDataAccess.java

@@ -1,4 +1,4 @@
-package org.hswebframework.web.service.authorization.simple.access;
+package org.hswebframework.web.authorization.simple;
 
 import org.hswebframework.web.authorization.access.ScriptDataAccessConfig;
 
@@ -7,15 +7,15 @@ import org.hswebframework.web.authorization.access.ScriptDataAccessConfig;
  *
  * @author zhouhao
  */
-public class SimpleScriptDataAccess extends AbstractDataAccess implements ScriptDataAccessConfig {
+public class SimpleScriptDataAccessConfig extends AbstractDataAccessConfig implements ScriptDataAccessConfig {
     private String script;
 
     private String scriptLanguage;
 
-    public SimpleScriptDataAccess() {
+    public SimpleScriptDataAccessConfig() {
     }
 
-    public SimpleScriptDataAccess(String script, String scriptLanguage) {
+    public SimpleScriptDataAccessConfig(String script, String scriptLanguage) {
         this.script = script;
         this.scriptLanguage = scriptLanguage;
     }

+ 50 - 0
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/SimpleUser.java

@@ -0,0 +1,50 @@
+package org.hswebframework.web.authorization.simple;
+
+import org.hswebframework.web.authorization.User;
+
+/**
+ * TODO 完成注释
+ *
+ * @author zhouhao
+ */
+public class SimpleUser implements User {
+    private String id;
+    private String username;
+    private String name;
+
+    public SimpleUser() {
+    }
+
+    public SimpleUser(String id, String username, String name) {
+        this.id = id;
+        this.username = username;
+        this.name = name;
+    }
+
+    @Override
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    @Override
+    public String getUsername() {
+        return username;
+    }
+
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+}

+ 13 - 0
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/builder/DataAccessConfigConvert.java

@@ -0,0 +1,13 @@
+package org.hswebframework.web.authorization.simple.builder;
+
+import org.hswebframework.web.authorization.access.DataAccessConfig;
+
+/**
+ * @author zhouhao
+ */
+public interface DataAccessConfigConvert {
+
+    boolean isSupport(String type, String action, String config);
+
+    DataAccessConfig convert(String type, String action, String config);
+}

+ 119 - 0
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/builder/SimpleAuthenticationBuilder.java

@@ -0,0 +1,119 @@
+package org.hswebframework.web.authorization.simple.builder;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import org.hswebframework.web.authorization.Authentication;
+import org.hswebframework.web.authorization.Permission;
+import org.hswebframework.web.authorization.Role;
+import org.hswebframework.web.authorization.User;
+import org.hswebframework.web.authorization.builder.AuthenticationBuilder;
+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.SimpleRole;
+import org.hswebframework.web.authorization.simple.SimpleUser;
+
+import java.io.Serializable;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * TODO 完成注释
+ *
+ * @author zhouhao
+ */
+public class SimpleAuthenticationBuilder implements AuthenticationBuilder {
+    private SimpleAuthentication authentication = new SimpleAuthentication();
+
+    private DataAccessConfigBuilderFactory dataBuilderFactory;
+
+    public SimpleAuthenticationBuilder(DataAccessConfigBuilderFactory dataBuilderFactory) {
+        this.dataBuilderFactory = dataBuilderFactory;
+    }
+
+    public void setDataBuilderFactory(DataAccessConfigBuilderFactory dataBuilderFactory) {
+        this.dataBuilderFactory = dataBuilderFactory;
+    }
+
+    @Override
+    public AuthenticationBuilder user(User user) {
+        Objects.requireNonNull(user);
+        authentication.setUser(user);
+        return this;
+    }
+
+    @Override
+    public AuthenticationBuilder user(String user) {
+        return user(JSON.parseObject(user, SimpleUser.class));
+    }
+
+    @Override
+    public AuthenticationBuilder user(Map<String, String> user) {
+        Objects.requireNonNull(user.get("id"));
+        user(new SimpleUser(user.get("id"), user.get("username"), user.get("name")));
+        return this;
+    }
+
+    @Override
+    public AuthenticationBuilder role(List<Role> role) {
+        authentication.setRoles(role);
+        return this;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public AuthenticationBuilder role(String role) {
+        return role((List) JSON.parseArray(role, SimpleRole.class));
+    }
+
+    @Override
+    public AuthenticationBuilder permission(List<Permission> permission) {
+        authentication.setPermissions(permission);
+        return this;
+    }
+
+    @Override
+    public AuthenticationBuilder permission(String permissionJson) {
+        JSONArray jsonArray = JSON.parseArray(permissionJson);
+        List<Permission> permissions = new ArrayList<>();
+        for (int i = 0; i < jsonArray.size(); i++) {
+            JSONObject jsonObject = jsonArray.getJSONObject(0);
+            SimplePermission permission = new SimplePermission();
+            permission.setId(jsonObject.getString("id"));
+            permission.setActions(new HashSet<>(jsonObject.getJSONArray("actions").toJavaList(String.class)));
+            permission.setDataAccesses(jsonObject.getJSONArray("dataAccesses").stream().map(JSONObject.class::cast)
+                    .map(dataJson -> dataBuilderFactory.create().fromJson(dataJson.toJSONString()).build())
+                    .collect(Collectors.toSet()));
+            permissions.add(permission);
+        }
+        authentication.setPermissions(permissions);
+        return this;
+    }
+
+    @Override
+    public AuthenticationBuilder attributes(String attributes) {
+        authentication.setAttributes(JSON.<Map<String, Serializable>>parseObject(attributes, Map.class));
+        return this;
+    }
+
+    @Override
+    public AuthenticationBuilder attributes(Map<String, Serializable> permission) {
+        authentication.setAttributes(permission);
+        return this;
+    }
+
+    @Override
+    public AuthenticationBuilder json(String json) {
+        JSONObject jsonObject = JSON.parseObject(json);
+        user(jsonObject.getObject("user", SimpleUser.class));
+        role(jsonObject.getJSONArray("roles").toJSONString());
+        permission(jsonObject.getJSONArray("permissions").toJSONString());
+        return this;
+    }
+
+    @Override
+    public Authentication build() {
+        return authentication;
+    }
+}

+ 24 - 0
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/builder/SimpleAuthenticationBuilderFactory.java

@@ -0,0 +1,24 @@
+package org.hswebframework.web.authorization.simple.builder;
+
+import org.hswebframework.web.authorization.builder.AuthenticationBuilder;
+import org.hswebframework.web.authorization.builder.AuthenticationBuilderFactory;
+import org.hswebframework.web.authorization.builder.DataAccessConfigBuilderFactory;
+
+/**
+ * TODO 完成注释
+ *
+ * @author zhouhao
+ */
+public class SimpleAuthenticationBuilderFactory implements AuthenticationBuilderFactory {
+
+    private DataAccessConfigBuilderFactory dataBuilderFactory;
+
+    public SimpleAuthenticationBuilderFactory(DataAccessConfigBuilderFactory dataBuilderFactory) {
+        this.dataBuilderFactory = dataBuilderFactory;
+    }
+
+    @Override
+    public AuthenticationBuilder create() {
+        return new SimpleAuthenticationBuilder(dataBuilderFactory);
+    }
+}

+ 47 - 0
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/builder/SimpleDataAccessConfigBuilder.java

@@ -0,0 +1,47 @@
+package org.hswebframework.web.authorization.simple.builder;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import org.hswebframework.web.authorization.access.DataAccessConfig;
+import org.hswebframework.web.authorization.builder.DataAccessConfigBuilder;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * @author zhouhao
+ */
+public class SimpleDataAccessConfigBuilder implements DataAccessConfigBuilder {
+    private String json;
+
+    private List<DataAccessConfigConvert> converts;
+
+    public SimpleDataAccessConfigBuilder(List<DataAccessConfigConvert> converts) {
+        Objects.requireNonNull(converts);
+        this.converts = converts;
+    }
+
+    @Override
+    public DataAccessConfigBuilder fromJson(String json) {
+        this.json = json;
+        return this;
+    }
+
+    @Override
+    public DataAccessConfig build() {
+        Objects.requireNonNull(json);
+        JSONObject jsonObject = JSON.parseObject(json);
+
+        String type = jsonObject.getString("type");
+        String action = jsonObject.getString("action");
+        String config = jsonObject.getString("config");
+
+        Objects.requireNonNull(type);
+        Objects.requireNonNull(action);
+
+
+        return converts.stream().filter(convert -> convert.isSupport(type, action, config))
+                .findAny().map(convert -> convert.convert(type, action, config))
+                .orElse(null);
+    }
+}

+ 94 - 0
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/builder/SimpleDataAccessConfigBuilderFactory.java

@@ -0,0 +1,94 @@
+package org.hswebframework.web.authorization.simple.builder;
+
+import com.alibaba.fastjson.JSON;
+import org.hswebframework.web.authorization.access.DataAccessConfig;
+import org.hswebframework.web.authorization.builder.DataAccessConfigBuilder;
+import org.hswebframework.web.authorization.builder.DataAccessConfigBuilderFactory;
+import org.hswebframework.web.authorization.simple.*;
+
+import javax.annotation.PostConstruct;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.BiFunction;
+
+import static org.hswebframework.web.authorization.access.DataAccessConfig.DefaultType.*;
+import static org.hswebframework.web.authorization.access.DataAccessConfig.DefaultType.CUSTOM;
+import static org.hswebframework.web.authorization.access.DataAccessConfig.DefaultType.OWN_CREATED;
+
+/**
+ * TODO 完成注释
+ *
+ * @author zhouhao
+ */
+public class SimpleDataAccessConfigBuilderFactory implements DataAccessConfigBuilderFactory {
+
+    private List<String> defaultSupportConvert = Arrays.asList(
+            CUSTOM,
+            OWN_CREATED,
+            FIELD_SCOPE,
+            DENY_FIELDS);
+
+    private List<DataAccessConfigConvert> converts = new LinkedList<>();
+
+    public SimpleDataAccessConfigBuilderFactory addConvert(DataAccessConfigConvert configBuilderConvert) {
+        Objects.requireNonNull(configBuilderConvert);
+        converts.add(configBuilderConvert);
+        return this;
+    }
+
+    public void setDefaultSupportConvert(List<String> defaultSupportConvert) {
+        this.defaultSupportConvert = defaultSupportConvert;
+    }
+
+    public List<String> getDefaultSupportConvert() {
+        return defaultSupportConvert;
+    }
+
+    protected DataAccessConfigConvert createJsonConfig(String supportType, Class<? extends AbstractDataAccessConfig> clazz) {
+        return createConfig(supportType, (action, config) -> JSON.parseObject(config, clazz));
+    }
+
+
+    protected DataAccessConfigConvert createConfig(String supportType, BiFunction<String, String, ? extends DataAccessConfig> function) {
+        return new DataAccessConfigConvert() {
+            @Override
+            public boolean isSupport(String type, String action, String config) {
+                return supportType.equals(type);
+            }
+
+            @Override
+            public DataAccessConfig convert(String type, String action, String config) {
+                DataAccessConfig conf = function.apply(action, config);
+                if (conf instanceof AbstractDataAccessConfig) {
+                    ((AbstractDataAccessConfig) conf).setAction(action);
+                }
+                return conf;
+            }
+        };
+    }
+
+    @PostConstruct
+    public void init() {
+        if (defaultSupportConvert.contains(FIELD_SCOPE))
+            converts.add(createJsonConfig(FIELD_SCOPE, SimpleFiledScopeDataAccessConfig.class));
+
+        if (defaultSupportConvert.contains(DENY_FIELDS))
+            converts.add(createJsonConfig(DENY_FIELDS, SimpleFieldFilterDataAccessConfig.class));
+
+        if (defaultSupportConvert.contains(OWN_CREATED))
+            converts.add(createConfig(OWN_CREATED, (action, config) -> new SimpleOwnCreatedDataAccessConfig(action)));
+
+        if (defaultSupportConvert.contains(SCRIPT))
+            converts.add(createJsonConfig(SCRIPT, SimpleScriptDataAccessConfig.class));
+
+        if (defaultSupportConvert.contains(CUSTOM))
+            converts.add(createConfig(CUSTOM, (action, config) -> new SimpleCustomDataAccessConfigConfig(config)));
+    }
+
+    @Override
+    public DataAccessConfigBuilder create() {
+        return new SimpleDataAccessConfigBuilder(converts);
+    }
+}

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

@@ -0,0 +1,3 @@
+# Auto Configure
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+org.hswebframework.web.authorization.simple.AuthorizationAutoConfiguration

+ 5 - 1
hsweb-authorization/hsweb-authorization-oauth2/hsweb-authorization-oauth2-auth-server/src/main/java/org/hswebframework/web/authorization/oauth2/server/client/OAuth2Client.java

@@ -38,7 +38,11 @@ public interface OAuth2Client {
 
     Long getCreateTime();
 
-    Boolean isEnabled();
+    /**
+     * @return 状态
+     * @see org.hswebframework.web.commons.entity.DataStatus
+     */
+    Byte getStatus();
 
     /**
      * @return 客户端支持的认证类型

+ 2 - 1
hsweb-authorization/hsweb-authorization-oauth2/hsweb-authorization-oauth2-auth-server/src/main/java/org/hswebframework/web/authorization/oauth2/server/support/AbstractAuthorizationService.java

@@ -22,6 +22,7 @@ import org.hswebframework.web.authorization.oauth2.server.client.OAuth2Client;
 import org.hswebframework.web.authorization.oauth2.server.client.OAuth2ClientService;
 import org.hswebframework.web.authorization.oauth2.server.exception.GrantTokenException;
 import org.hswebframework.web.authorization.oauth2.server.token.AccessTokenService;
+import org.hswebframework.web.commons.entity.DataStatus;
 import org.hswebframework.web.oauth2.core.ErrorType;
 
 import static org.hswebframework.web.oauth2.core.ErrorType.*;
@@ -75,7 +76,7 @@ public abstract class AbstractAuthorizationService {
         if (client == null) {
             throw new GrantTokenException(CLIENT_NOT_EXIST);
         }
-        if (Boolean.TRUE != client.isEnabled()) {
+        if (DataStatus.STATUS_ENABLED != client.getStatus()) {
             throw new GrantTokenException(CLIENT_DISABLED);
         }
         return client;

+ 1 - 1
hsweb-authorization/hsweb-authorization-oauth2/hsweb-authorization-oauth2-auth-server/src/main/java/org/hswebframework/web/authorization/oauth2/server/support/HttpTokenRequest.java

@@ -23,7 +23,7 @@ import org.hswebframework.web.authorization.oauth2.server.TokenRequest;
 import org.hswebframework.web.authorization.oauth2.server.exception.GrantTokenException;
 import org.hswebframework.web.oauth2.core.ErrorType;
 import org.hswebframework.web.oauth2.core.OAuth2Constants;
-import org.hswebframwork.utils.StringUtils;
+import org.hswebframework.utils.StringUtils;
 
 import javax.servlet.http.HttpServletRequest;
 import java.util.*;

+ 6 - 0
hsweb-authorization/hsweb-authorization-oauth2/hsweb-authorization-oauth2-client/src/main/java/org/hswebframework/web/authorization/oauth2/client/AccessTokenInfo.java

@@ -17,6 +17,8 @@
  */
 package org.hswebframework.web.authorization.oauth2.client;
 
+import com.alibaba.fastjson.annotation.JSONField;
+
 /**
  * 默认的服务实现
  *
@@ -24,10 +26,13 @@ package org.hswebframework.web.authorization.oauth2.client;
  */
 public class AccessTokenInfo {
     //授权码
+    @JSONField(name = "access_token")
     private String  accessToken;
     //更新码
+    @JSONField(name = "refresh_token")
     private String  refreshToken;
     //有效期
+    @JSONField(name = "expires_in")
     private Integer expiresIn;
     //授权范围
     private String  scope;
@@ -36,6 +41,7 @@ public class AccessTokenInfo {
 
     private Long updateTime;
 
+    @JSONField(name = "token_type")
     private String tokenType;
 
     public boolean isExpire() {

+ 5 - 7
hsweb-authorization/hsweb-authorization-shiro/README.md

@@ -10,11 +10,10 @@
 | [`@Authorize`](../hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/annotation/Authorize.java)    |      [ExpressionAnnotationMethodInterceptor](src/main/java/org/hswebframework/web/authorization/shiro/boost/SimpleAuthorizeMethodInterceptor.java)        |
 | [`@RequiresExpression`](../hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/annotation/RequiresExpression.java)    | [ExpressionAnnotationMethodInterceptor](src/main/java/org/hswebframework/web/authorization/shiro/boost/ExpressionAnnotationMethodInterceptor.java)      | 
 | [`@RequiresDataAccess`](../hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/annotation/RequiresDataAccess.java)    | [DataAccessAnnotationMethodInterceptor](src/main/java/org/hswebframework/web/authorization/shiro/boost/DataAccessAnnotationMethodInterceptor.java)      | 
-| [`@RequiresFieldAccess`](../hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/annotation/RequiresFieldAccess.java)  | [FieldAccessAnnotationMethodInterceptor](src/main/java/org/hswebframework/web/authorization/shiro/boost/FieldAccessAnnotationMethodInterceptor.java)   | 
 
 ## 拓展接口
 
-### 级权限控制器
+### 数据级权限控制器
 
 控制逻辑简述:
 
@@ -24,15 +23,14 @@
 
 可自己实现DataAccessHandler接口并注入spring以实现自定义的控制方式
 
-现已实现3种控制器
+内置的控制方式
 
 1. [CustomDataAccessHandler](src/main/java/org/hswebframework/web/authorization/shiro/boost/handler/CustomDataAccessHandler.java) 自定义控制器
 2. [OwnCreatedDataAccessHandler](src/main/java/org/hswebframework/web/authorization/shiro/boost/handler/OwnCreatedDataAccessHandler.java) 控制只能操作自己创建的数据
 3. [ScriptDataAccessHandler](src/main/java/org/hswebframework/web/authorization/shiro/boost/handler/ScriptDataAccessHandler.java) 使用脚本方式控制
+4. [FieldScopeDataAccessHandler](src/main/java/org/hswebframework/web/authorization/shiro/boost/handler/FieldScopeDataAccessHandler.java) 控制字段的值范围,如: orgId in (1,2,3,4)
+5. [FieldFilterDataAccessHandler](src/main/java/org/hswebframework/web/authorization/shiro/boost/handler/FieldFilterDataAccessHandler.java) 控制字段的操作范围,此控制器替代之前的FieldAccess功能
 
-注意: 控制需满足的条件请查看控制器源代码查看注释获取
 
-### 列级别控制器
-控制逻辑和行级类似
+注意: 控制需满足的条件请查看控制器源代码查看注释获取
 
-提供默认的控制器 [DefaultFieldAccessController](src/main/java/org/hswebframework/web/authorization/shiro/boost/DefaultFieldAccessController.java) 

+ 20 - 16
hsweb-authorization/hsweb-authorization-shiro/src/main/java/org/hswebframework/web/authorization/shiro/ShiroAutoConfiguration.java

@@ -35,10 +35,8 @@ import org.hswebframework.web.authorization.AuthenticationManager;
 import org.hswebframework.web.authorization.AuthenticationSupplier;
 import org.hswebframework.web.authorization.access.DataAccessController;
 import org.hswebframework.web.authorization.access.DataAccessHandler;
-import org.hswebframework.web.authorization.access.FieldAccessController;
 import org.hswebframework.web.authorization.shiro.boost.BoostAuthorizationAttributeSourceAdvisor;
 import org.hswebframework.web.authorization.shiro.boost.DefaultDataAccessController;
-import org.hswebframework.web.authorization.shiro.boost.DefaultFieldAccessController;
 import org.hswebframework.web.authorization.shiro.cache.SpringCacheManagerWrapper;
 import org.hswebframework.web.authorization.shiro.remember.SimpleRememberMeManager;
 import org.hswebframework.web.controller.message.ResponseMessage;
@@ -71,9 +69,6 @@ public class ShiroAutoConfiguration {
     @Autowired(required = false)
     private org.springframework.cache.CacheManager cacheManager;
 
-    @Autowired(required = false)
-    private List<DataAccessHandler> dataAccessHandlers;
-
     @Bean
     public CacheManager shiroCacheManager() {
         if (cacheManager == null) {
@@ -153,24 +148,33 @@ public class ShiroAutoConfiguration {
     @Bean
     @ConditionalOnMissingBean
     public DefaultDataAccessController defaultDataAccessController() {
-        DefaultDataAccessController accessController = new DefaultDataAccessController();
-        if (dataAccessHandlers != null) {
-            dataAccessHandlers.forEach(accessController::addHandler);
-        }
-        return accessController;
+        return new DefaultDataAccessController();
     }
 
     @Bean
-    @ConditionalOnMissingBean
-    public DefaultFieldAccessController defaultFieldAccessController() {
-        return new DefaultFieldAccessController();
+    @ConditionalOnBean(DefaultDataAccessController.class)
+    public BeanPostProcessor dataAccessControllerProcessor(DefaultDataAccessController defaultDataAccessController) {
+        return new BeanPostProcessor() {
+            @Override
+            public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
+                return bean;
+            }
+
+            @Override
+            public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
+                if (bean instanceof DataAccessHandler) {
+                    defaultDataAccessController.addHandler(((DataAccessHandler) bean));
+                }
+                return bean;
+            }
+        };
     }
 
+
     @Bean
     public BoostAuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager,
-                                                                                        DataAccessController dataAccessController,
-                                                                                        FieldAccessController fieldAccessController) {
-        BoostAuthorizationAttributeSourceAdvisor advisor = new BoostAuthorizationAttributeSourceAdvisor(dataAccessController, fieldAccessController);
+                                                                                        DataAccessController dataAccessController) {
+        BoostAuthorizationAttributeSourceAdvisor advisor = new BoostAuthorizationAttributeSourceAdvisor(dataAccessController);
         advisor.setSecurityManager(securityManager);
         return advisor;
     }

+ 11 - 12
hsweb-authorization/hsweb-authorization-shiro/src/main/java/org/hswebframework/web/authorization/shiro/boost/BoostAuthorizationAttributeSourceAdvisor.java

@@ -26,15 +26,11 @@ import org.apache.shiro.spring.security.interceptor.AopAllianceAnnotationsAuthor
 import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
 import org.hswebframework.web.AopUtils;
 import org.hswebframework.web.authorization.access.DataAccessController;
-import org.hswebframework.web.authorization.access.FieldAccessController;
 import org.hswebframework.web.authorization.annotation.Authorize;
 import org.hswebframework.web.authorization.annotation.RequiresDataAccess;
 import org.hswebframework.web.authorization.annotation.RequiresExpression;
-import org.hswebframework.web.authorization.annotation.RequiresFieldAccess;
 import org.hswebframework.web.boost.aop.context.MethodInterceptorHolder;
 import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor;
-import org.springframework.core.Ordered;
-import org.springframework.core.annotation.AnnotationUtils;
 
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Method;
@@ -57,8 +53,7 @@ public class BoostAuthorizationAttributeSourceAdvisor extends StaticMethodMatche
                     //自定义
                     RequiresExpression.class,
                     Authorize.class,
-                    RequiresDataAccess.class,
-                    RequiresFieldAccess.class
+                    RequiresDataAccess.class
             };
 
     protected SecurityManager securityManager = null;
@@ -66,11 +61,9 @@ public class BoostAuthorizationAttributeSourceAdvisor extends StaticMethodMatche
     /**
      * Create a new AuthorizationAttributeSourceAdvisor.
      *
-     * @param dataAccessController  数据权限控制器
-     * @param fieldAccessController 字段权限控制器
+     * @param dataAccessController 数据权限控制器
      */
-    public BoostAuthorizationAttributeSourceAdvisor(DataAccessController dataAccessController,
-                                                    FieldAccessController fieldAccessController) {
+    public BoostAuthorizationAttributeSourceAdvisor(DataAccessController dataAccessController) {
         AopAllianceAnnotationsAuthorizingMethodInterceptor interceptor =
                 new AopAllianceAnnotationsAuthorizingMethodInterceptor() {
                     @Override
@@ -84,8 +77,6 @@ public class BoostAuthorizationAttributeSourceAdvisor extends StaticMethodMatche
         interceptor.getMethodInterceptors().add(new ExpressionAnnotationMethodInterceptor(resolver));
         // @RequiresDataAccess support
         interceptor.getMethodInterceptors().add(new DataAccessAnnotationMethodInterceptor(dataAccessController, resolver));
-        // @RequiresFieldAccess support
-        interceptor.getMethodInterceptors().add(new FieldAccessAnnotationMethodInterceptor(fieldAccessController, resolver));
         // @Authorize support
         interceptor.getMethodInterceptors().add(new SimpleAuthorizeMethodInterceptor(resolver));
         setAdvice(interceptor);
@@ -100,6 +91,14 @@ public class BoostAuthorizationAttributeSourceAdvisor extends StaticMethodMatche
     }
 
     public boolean matches(Method method, Class targetClass) {
+        Authorize authorize = AopUtils.findMethodAnnotation(targetClass, method, Authorize.class);
+        if (null != authorize) {
+            if (authorize.ignore()) return false;
+        }
+        authorize = AopUtils.findAnnotation(targetClass, Authorize.class);
+        if (null != authorize) {
+            if (authorize.ignore()) return false;
+        }
         return Arrays.stream(AUTHZ_ANNOTATION_CLASSES)
                 .anyMatch(aClass -> AopUtils.findAnnotation(targetClass, method, aClass) != null);
     }

+ 31 - 7
hsweb-authorization/hsweb-authorization-shiro/src/main/java/org/hswebframework/web/authorization/shiro/boost/DataAccessAnnotationMethodInterceptor.java

@@ -18,6 +18,7 @@
 package org.hswebframework.web.authorization.shiro.boost;
 
 import org.apache.shiro.aop.AnnotationResolver;
+import org.apache.shiro.authc.AuthenticationException;
 import org.apache.shiro.authz.AuthorizationException;
 import org.apache.shiro.authz.aop.AuthorizingAnnotationHandler;
 import org.apache.shiro.authz.aop.AuthorizingAnnotationMethodInterceptor;
@@ -27,11 +28,12 @@ import org.hswebframework.web.authorization.Authentication;
 import org.hswebframework.web.authorization.Permission;
 import org.hswebframework.web.authorization.access.DataAccessConfig;
 import org.hswebframework.web.authorization.access.DataAccessController;
+import org.hswebframework.web.authorization.annotation.Authorize;
 import org.hswebframework.web.authorization.annotation.Logical;
 import org.hswebframework.web.authorization.annotation.RequiresDataAccess;
 import org.hswebframework.web.boost.aop.context.MethodInterceptorHolder;
 import org.hswebframework.web.boost.aop.context.MethodInterceptorParamContext;
-import org.hswebframwork.utils.StringUtils;
+import org.hswebframework.utils.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -44,7 +46,7 @@ import java.util.stream.Collectors;
 /**
  * 数据级权限控制实现 <br>
  * 通过在方法上注解{@link RequiresDataAccess},标识需要进行数据级权限控制<br>
- * 控制的方式和规则由 {@link Permission#getDataAccessConfigs()}实现<br>
+ * 控制的方式和规则由 {@link Permission#getDataAccesses()}实现<br>
  *
  * @author zhouhao
  * @see DefaultDataAccessController
@@ -67,7 +69,7 @@ public class DataAccessAnnotationMethodInterceptor extends AuthorizingAnnotation
             this.dataAccessController = controller;
         }
 
-        Map<Class<DataAccessController>, DataAccessController> cache = new HashMap<>();
+        final Map<Class<DataAccessController>, DataAccessController> cache = new HashMap<>(128);
 
         @Override
         public void assertAuthorized(Annotation a) throws AuthorizationException {
@@ -99,14 +101,36 @@ public class DataAccessAnnotationMethodInterceptor extends AuthorizingAnnotation
                 accessController = ApplicationContextHolder.get().getBean(accessAnn.controllerBeanName(), DataAccessController.class);
             }
             DataAccessController finalAccessController = accessController;
+            Authorize classAnnotation = holder.findClassAnnotation(Authorize.class);
+            Authorize methodAnnotation = holder.findMethodAnnotation(Authorize.class);
+            Set<String> permissions = new HashSet<>();
+            List<String> actionList = new ArrayList<>(Arrays.asList(accessAnn.action()));
+
+            if (classAnnotation != null) {
+                permissions.addAll(Arrays.asList(classAnnotation.permission()));
+                if (actionList.isEmpty())
+                    actionList.addAll(Arrays.asList(classAnnotation.action()));
+            }
+            if (methodAnnotation != null) {
+                permissions.addAll(Arrays.asList(methodAnnotation.permission()));
+                if (actionList.isEmpty())
+                    actionList.addAll(Arrays.asList(methodAnnotation.action()));
+            }
 
-            MethodInterceptorParamContext context = holder.createParamContext();
             String permission = accessAnn.permission();
-            Permission permissionInfo = authentication.getPermission(permission);
-            List<String> actionList = Arrays.asList(accessAnn.action());
+
+            if ("".equals(permission)) {
+                if (permissions.size() != 1) {
+                    throw new IndexOutOfBoundsException("permission setting size must be 1");
+                }
+                permission = permissions.iterator().next();
+            }
+            MethodInterceptorParamContext context = holder.createParamContext();
+            Permission permissionInfo = authentication.getPermission(permission).orElseThrow(AuthenticationException::new);
+
             //取得当前登录用户持有的控制规则
             Set<DataAccessConfig> accesses = permissionInfo
-                    .getDataAccessConfigs()
+                    .getDataAccesses()
                     .stream()
                     .filter(access -> actionList.contains(access.getAction()))
                     .collect(Collectors.toSet());

+ 4 - 4
hsweb-authorization/hsweb-authorization-shiro/src/main/java/org/hswebframework/web/authorization/shiro/boost/DefaultDataAccessController.java

@@ -3,9 +3,7 @@ package org.hswebframework.web.authorization.shiro.boost;
 import org.hswebframework.web.authorization.access.DataAccessConfig;
 import org.hswebframework.web.authorization.access.DataAccessController;
 import org.hswebframework.web.authorization.access.DataAccessHandler;
-import org.hswebframework.web.authorization.shiro.boost.handler.CustomDataAccessHandler;
-import org.hswebframework.web.authorization.shiro.boost.handler.OwnCreatedDataAccessHandler;
-import org.hswebframework.web.authorization.shiro.boost.handler.ScriptDataAccessHandler;
+import org.hswebframework.web.authorization.shiro.boost.handler.*;
 import org.hswebframework.web.boost.aop.context.MethodInterceptorParamContext;
 
 import java.util.LinkedList;
@@ -34,6 +32,8 @@ public final class DefaultDataAccessController implements DataAccessController {
         addHandler(new CustomDataAccessHandler());
         addHandler(new OwnCreatedDataAccessHandler());
         addHandler(new ScriptDataAccessHandler());
+        addHandler(new FieldFilterDataAccessHandler());
+        addHandler(new FieldScopeDataAccessHandler());
     }
 
     @Override
@@ -42,7 +42,7 @@ public final class DefaultDataAccessController implements DataAccessController {
         return handlers.parallelStream()
                 // TODO: 17-3-28 可以换成access对应的handler以提高效率
                 .filter(handler -> handler.isSupport(access))
-                .anyMatch(handler -> handler.handle(access, params));
+                .allMatch(handler -> handler.handle(access, params));
     }
 
     public DefaultDataAccessController addHandler(DataAccessHandler handler) {

+ 0 - 99
hsweb-authorization/hsweb-authorization-shiro/src/main/java/org/hswebframework/web/authorization/shiro/boost/DefaultFieldAccessController.java

@@ -1,99 +0,0 @@
-package org.hswebframework.web.authorization.shiro.boost;
-
-import org.apache.commons.beanutils.BeanUtilsBean;
-import org.hswebframework.web.authorization.Permission;
-import org.hswebframework.web.authorization.access.FieldAccessConfig;
-import org.hswebframework.web.authorization.access.FieldAccessController;
-import org.hswebframework.web.boost.aop.context.MethodInterceptorParamContext;
-import org.hswebframework.web.commons.entity.Entity;
-import org.hswebframework.web.commons.entity.RecordCreationEntity;
-import org.hswebframework.web.commons.entity.param.QueryParamEntity;
-import org.hswebframework.web.commons.model.Model;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.Set;
-
-/**
- * 默认的字段级权限控制,目前已实现提供对查询(query),更新(update)的权限控制。
- * 控制方式主要是通过被拦截方法的参数类型进行识别,如果是可进行控制的参数类型,则通过修改参数属性等方式,进行控制。
- *
- * @author zhouhao
- * @see FieldAccessController
- * @since 3.0
- */
-public class DefaultFieldAccessController implements FieldAccessController {
-
-    private Logger logger = LoggerFactory.getLogger(DefaultFieldAccessController.class);
-
-    @Override
-    public boolean doAccess(String action, Set<FieldAccessConfig> accesses, MethodInterceptorParamContext params) {
-        //控制转发
-        switch (action) {
-            case Permission.ACTION_QUERY:
-                return doQueryAccess(accesses, params);
-            case Permission.ACTION_UPDATE:
-                return doUpdateAccess(accesses, params);
-            default:
-                logger.warn("action {} not support now!", action);
-        }
-        return false;
-    }
-
-    /**
-     * 执行更新操作的控制,此方法永远返回true.通过取得参数中实现{@link Entity}的参数,将把这个参数实体所对应不能操作的字段全部设置为null。 <br>
-     * 注意: 此方式还需要dao框架的支持(为null的字段不进行更新) <br>
-     * 如果没有{@link Entity}的参数,则不进行控制并给出警告信息
-     *
-     * @param accesses 不可操作的字段
-     * @param params   参数上下文
-     * @return true
-     * @see BeanUtilsBean
-     * @see org.apache.commons.beanutils.PropertyUtilsBean
-     */
-    protected boolean doUpdateAccess(Set<FieldAccessConfig> accesses, MethodInterceptorParamContext params) {
-        Object supportParam = params.getParams().values().stream()
-                .filter(param -> (param instanceof Entity) | (param instanceof Model))
-                .findAny().orElse(null);
-        if (null != supportParam) {
-            for (FieldAccessConfig access : accesses) {
-                try {
-                    //设置值为null,跳过修改
-                    BeanUtilsBean.getInstance()
-                            .getPropertyUtils()
-                            .setProperty(supportParam, access.getField(), null);
-                } catch (Exception e) {
-                }
-            }
-            if (supportParam instanceof RecordCreationEntity) {
-                RecordCreationEntity creationEntity = ((RecordCreationEntity) supportParam);
-                creationEntity.setCreateTime(null);
-                creationEntity.setCreatorId(null);
-            }
-        } else {
-            logger.warn("doUpdateAccess skip ,because can not found any entity in param!");
-        }
-        return true;
-    }
-
-    /**
-     * 执行查询的控制,查询主要针对参数为{@link QueryParamEntity}的动态条件查询,通过设置{@link QueryParamEntity#excludes(String...)}.指定不需要查询的字段
-     * 如果没有{@link QueryParamEntity}的参数,则不进行控制并给出警告信息
-     *
-     * @param accesses 不能查询的字段
-     * @param params   参数上下文
-     * @return true
-     */
-    protected boolean doQueryAccess(Set<FieldAccessConfig> accesses, MethodInterceptorParamContext params) {
-        QueryParamEntity paramEntity = params.getParams().values().stream()
-                .filter(QueryParamEntity.class::isInstance)
-                .map(QueryParamEntity.class::cast)
-                .findAny().orElse(null);
-        if (paramEntity != null) {
-            paramEntity.excludes(accesses.stream().map(FieldAccessConfig::getField).toArray(String[]::new));
-        } else {
-            logger.warn("doQueryAccess skip ,because can not found any QueryParamEntity in param!");
-        }
-        return true;
-    }
-}

+ 0 - 88
hsweb-authorization/hsweb-authorization-shiro/src/main/java/org/hswebframework/web/authorization/shiro/boost/FieldAccessAnnotationMethodInterceptor.java

@@ -1,88 +0,0 @@
-/*
- * Copyright 2016 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.shiro.boost;
-
-import org.apache.shiro.aop.AnnotationResolver;
-import org.apache.shiro.authz.AuthorizationException;
-import org.apache.shiro.authz.aop.AuthorizingAnnotationHandler;
-import org.apache.shiro.authz.aop.AuthorizingAnnotationMethodInterceptor;
-import org.hswebframework.web.authorization.Authentication;
-import org.hswebframework.web.authorization.AuthenticationHolder;
-import org.hswebframework.web.authorization.Permission;
-import org.hswebframework.web.authorization.access.FieldAccessConfig;
-import org.hswebframework.web.authorization.access.FieldAccessController;
-import org.hswebframework.web.authorization.annotation.RequiresFieldAccess;
-import org.hswebframework.web.boost.aop.context.MethodInterceptorHolder;
-import org.hswebframework.web.boost.aop.context.MethodInterceptorParamContext;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.lang.annotation.Annotation;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-/**
- * TODO 完成注释
- *
- * @author zhouhao
- */
-public class FieldAccessAnnotationMethodInterceptor extends AuthorizingAnnotationMethodInterceptor {
-
-    public FieldAccessAnnotationMethodInterceptor(FieldAccessController controller, AnnotationResolver resolver) {
-        super(new DataAccessAnnotationHandler(controller), resolver);
-    }
-
-    private static final Logger logger = LoggerFactory.getLogger(FieldAccessAnnotationMethodInterceptor.class);
-
-    static class DataAccessAnnotationHandler extends AuthorizingAnnotationHandler {
-        protected FieldAccessController fieldAccessController;
-
-        public DataAccessAnnotationHandler(FieldAccessController controller) {
-            super(RequiresFieldAccess.class);
-            this.fieldAccessController = controller;
-        }
-
-        @Override
-        public void assertAuthorized(Annotation a) throws AuthorizationException {
-            if (!(a instanceof RequiresFieldAccess)) return;
-            MethodInterceptorHolder holder = MethodInterceptorHolder.current();
-            if (null == holder) {
-                logger.warn("MethodInterceptorHolder is null!");
-                return;
-            }
-            RequiresFieldAccess accessAnn = ((RequiresFieldAccess) a);
-            MethodInterceptorParamContext context = holder.createParamContext();
-            Authentication authentication = Authentication
-                    .current()
-                    .orElseThrow(AuthorizationException::new);
-            
-            String permission = accessAnn.permission();
-            Permission permissionInfo = authentication.getPermission(permission);
-
-            Set<FieldAccessConfig> accesses = permissionInfo
-                    .getFieldAccesses()
-                    .stream()
-                    .filter(access -> access.getActions().contains(accessAnn.action()))
-                    .collect(Collectors.toSet());
-            boolean isAccess = fieldAccessController.doAccess(accessAnn.action(), accesses, context);
-            if (!isAccess) {
-                throw new AuthorizationException("{access_deny}");
-            }
-        }
-    }
-}

+ 28 - 22
hsweb-authorization/hsweb-authorization-shiro/src/main/java/org/hswebframework/web/authorization/shiro/boost/SimpleAuthorizeMethodInterceptor.java

@@ -18,23 +18,20 @@
 
 package org.hswebframework.web.authorization.shiro.boost;
 
-import org.apache.commons.codec.digest.DigestUtils;
 import org.apache.shiro.aop.AnnotationResolver;
 import org.apache.shiro.authz.AuthorizationException;
 import org.apache.shiro.authz.UnauthenticatedException;
 import org.apache.shiro.authz.aop.AuthorizingAnnotationHandler;
 import org.apache.shiro.authz.aop.AuthorizingAnnotationMethodInterceptor;
-import org.hswebframework.expands.script.engine.DynamicScriptEngine;
-import org.hswebframework.expands.script.engine.DynamicScriptEngineFactory;
+import org.hswebframework.utils.ClassUtils;
+import org.hswebframework.utils.StringUtils;
+import org.hswebframework.web.ExpressionUtils;
 import org.hswebframework.web.authorization.Authentication;
-import org.hswebframework.web.authorization.AuthenticationHolder;
 import org.hswebframework.web.authorization.Permission;
 import org.hswebframework.web.authorization.Role;
 import org.hswebframework.web.authorization.annotation.Authorize;
 import org.hswebframework.web.authorization.annotation.Logical;
 import org.hswebframework.web.boost.aop.context.MethodInterceptorHolder;
-import org.hswebframwork.utils.ClassUtils;
-import org.hswebframwork.utils.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -56,6 +53,7 @@ public class SimpleAuthorizeMethodInterceptor extends AuthorizingAnnotationMetho
 
     private static final Logger logger = LoggerFactory.getLogger(SimpleAuthorizeMethodInterceptor.class);
 
+
     static class AuthorizeAnnotationHandler extends AuthorizingAnnotationHandler {
 
         public AuthorizeAnnotationHandler() {
@@ -72,9 +70,12 @@ public class SimpleAuthorizeMethodInterceptor extends AuthorizingAnnotationMetho
             }
             AuthorizeConfig authorizeConfig = new AuthorizeConfig(holder.getArgs());
             Authorize authorize = ((Authorize) a);
+            if (authorize.ignore()) return;
+
             if (authorize.merge()) {
                 Authorize classAnn = ClassUtils.getAnnotation(holder.getTarget().getClass(), Authorize.class);
                 if (null != classAnn) {
+                    if (classAnn.ignore()) return;
                     authorizeConfig.put(classAnn);
                 }
             }
@@ -100,7 +101,7 @@ public class SimpleAuthorizeMethodInterceptor extends AuthorizingAnnotationMetho
                                     .filter(authorizeConfig.action::contains)
                                     .collect(Collectors.toList());
                             //如果 控制逻辑是or,则只要过滤结果数量不为0.否则过滤结果数量必须和配置的数量相同
-                            return logicalIsOr ? actions.size() > 0 : actions.size() == permission.getActions().size();
+                            return logicalIsOr ? actions.size() > 0 : permission.getActions().containsAll(actions);
                         }).collect(Collectors.toList());
                 access = logicalIsOr ? permissions.size() > 0 : permissions.size() == authorizeConfig.permission.size();
             }
@@ -150,22 +151,27 @@ public class SimpleAuthorizeMethodInterceptor extends AuthorizingAnnotationMetho
         }
 
         public String tryCompileExpression(String express) {
-            if (express.startsWith("${") && express.endsWith("}")) {
-                express = express.substring(2, express.length() - 1);
-                DynamicScriptEngine spelEngine = DynamicScriptEngineFactory.getEngine("spel");
-                String id = DigestUtils.md5Hex(express);
-                try {
-                    if (!spelEngine.compiled(id))
-                        spelEngine.compile(id, express);
-                    return String.valueOf(spelEngine.execute(id, var).getIfSuccess());
-                } catch (Exception e) {
-                    throw new AuthorizationException("系统错误", e);
-                } finally {
-                    //     spelEngine.remove(id);
-                }
-            } else {
-                return express;
+            try {
+                return ExpressionUtils.analytical(express, var, "spel");
+            } catch (Exception e) {
+                throw new AuthorizationException("系统错误", e);
             }
+//            if (express.startsWith("${") && express.endsWith("}")) {
+//                express = express.substring(2, express.length() - 1);
+//                DynamicScriptEngine spelEngine = DynamicScriptEngineFactory.getEngine("spel");
+//                String id = DigestUtils.md5Hex(express);
+//                try {
+//                    if (!spelEngine.compiled(id))
+//                        spelEngine.compile(id, express);
+//                    return String.valueOf(spelEngine.execute(id, var).getIfSuccess());
+//                } catch (Exception e) {
+//                    throw new AuthorizationException("系统错误", e);
+//                } finally {
+//                    //     spelEngine.remove(id);
+//                }
+//            } else {
+//                return express;
+//            }
         }
 
         public Collection<String> tryCompileExpression(String... expresses) {

+ 3 - 3
hsweb-authorization/hsweb-authorization-shiro/src/main/java/org/hswebframework/web/authorization/shiro/boost/handler/CustomDataAccessHandler.java

@@ -22,7 +22,7 @@ import org.hswebframework.web.authorization.access.*;
 import org.hswebframework.web.boost.aop.context.MethodInterceptorParamContext;
 
 /**
- * 当配置为自定义处理器时(实现{@link CustomDataAccess }接口),此处理器生效
+ * 当配置为自定义处理器时(实现{@link CustomDataAccessConfig }接口),此处理器生效
  *
  * @author zhouhao
  * @see 3.0
@@ -31,12 +31,12 @@ public class CustomDataAccessHandler implements DataAccessHandler {
 
     @Override
     public boolean isSupport(DataAccessConfig access) {
-        return access instanceof CustomDataAccess;
+        return access instanceof CustomDataAccessConfig;
     }
 
     @Override
     public boolean handle(DataAccessConfig access, MethodInterceptorParamContext context) {
-        CustomDataAccess custom = ((CustomDataAccess) access);
+        CustomDataAccessConfig custom = ((CustomDataAccessConfig) access);
         return custom.getController().doAccess(access, context);
     }
 }

+ 86 - 0
hsweb-authorization/hsweb-authorization-shiro/src/main/java/org/hswebframework/web/authorization/shiro/boost/handler/FieldFilterDataAccessHandler.java

@@ -0,0 +1,86 @@
+package org.hswebframework.web.authorization.shiro.boost.handler;
+
+import org.apache.commons.beanutils.BeanUtilsBean;
+import org.hswebframework.web.authorization.Permission;
+import org.hswebframework.web.authorization.access.DataAccessConfig;
+import org.hswebframework.web.authorization.access.DataAccessHandler;
+import org.hswebframework.web.authorization.access.FieldFilterDataAccessConfig;
+import org.hswebframework.web.boost.aop.context.MethodInterceptorParamContext;
+import org.hswebframework.web.commons.entity.Entity;
+import org.hswebframework.web.commons.entity.param.QueryParamEntity;
+import org.hswebframework.web.commons.model.Model;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * 数据权限字段过滤处理,目前仅支持deny. {@link DataAccessConfig.DefaultType#DENY_FIELDS}
+ *
+ * @author zhouhao
+ */
+public class FieldFilterDataAccessHandler implements DataAccessHandler {
+    private Logger logger = LoggerFactory.getLogger(this.getClass());
+
+    @Override
+    public boolean isSupport(DataAccessConfig access) {
+        return access instanceof FieldFilterDataAccessConfig && DataAccessConfig.DefaultType.DENY_FIELDS.equals(access.getType());
+    }
+
+    @Override
+    public boolean handle(DataAccessConfig access, MethodInterceptorParamContext context) {
+        FieldFilterDataAccessConfig filterDataAccessConfig = ((FieldFilterDataAccessConfig) access);
+
+        switch (access.getAction()) {
+            case Permission.ACTION_QUERY:
+                return doQueryAccess(filterDataAccessConfig, context);
+            case Permission.ACTION_UPDATE:
+                return doUpdateAccess(filterDataAccessConfig, context);
+            default:
+                if (logger.isDebugEnabled())
+                    logger.debug("field filter not support for {}", access.getAction());
+                return true;
+        }
+    }
+
+    /**
+     * @param accesses 不可操作的字段
+     * @param params   参数上下文
+     * @return true
+     * @see BeanUtilsBean
+     * @see org.apache.commons.beanutils.PropertyUtilsBean
+     */
+    protected boolean doUpdateAccess(FieldFilterDataAccessConfig accesses, MethodInterceptorParamContext params) {
+        Object supportParam = params.getParams().values().stream()
+                .filter(param -> (param instanceof Entity) | (param instanceof Model))
+                .findAny().orElse(null);
+        if (null != supportParam) {
+            for (String field : accesses.getFields()) {
+                try {
+                    //设置值为null,跳过修改
+                    BeanUtilsBean.getInstance()
+                            .getPropertyUtils()
+                            .setProperty(supportParam, field, null);
+                } catch (Exception e) {
+                    logger.warn("can't set {} null", field, e);
+                }
+            }
+        } else {
+            logger.warn("doUpdateAccess skip ,because can not found any entity in param!");
+        }
+        return true;
+    }
+
+
+    protected boolean doQueryAccess(FieldFilterDataAccessConfig access, MethodInterceptorParamContext context) {
+        QueryParamEntity entity = context.getParams()
+                .values().stream()
+                .filter(QueryParamEntity.class::isInstance)
+                .map(QueryParamEntity.class::cast)
+                .findAny().orElse(null);
+        if (entity == null) {
+            logger.warn("try validate query access, but query entity is null or not instance of org.hswebframework.web.commons.entity.Entity");
+            return true;
+        }
+        entity.excludes(access.getFields().toArray(new String[access.getFields().size()]));
+        return true;
+    }
+}

+ 114 - 0
hsweb-authorization/hsweb-authorization-shiro/src/main/java/org/hswebframework/web/authorization/shiro/boost/handler/FieldScopeDataAccessHandler.java

@@ -0,0 +1,114 @@
+package org.hswebframework.web.authorization.shiro.boost.handler;
+
+import org.apache.commons.beanutils.BeanUtilsBean;
+import org.apache.commons.beanutils.PropertyUtilsBean;
+import org.hsweb.ezorm.core.param.Term;
+import org.hsweb.ezorm.core.param.TermType;
+import org.hswebframework.web.authorization.Permission;
+import org.hswebframework.web.authorization.access.DataAccessConfig;
+import org.hswebframework.web.authorization.access.DataAccessHandler;
+import org.hswebframework.web.authorization.access.FieldScopeDataAccessConfig;
+import org.hswebframework.web.authorization.annotation.RequiresDataAccess;
+import org.hswebframework.web.boost.aop.context.MethodInterceptorParamContext;
+import org.hswebframework.web.commons.entity.param.QueryParamEntity;
+import org.hswebframework.web.controller.QueryController;
+import org.hswebframework.web.service.QueryService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author zhouhao
+ */
+public class FieldScopeDataAccessHandler implements DataAccessHandler {
+    private PropertyUtilsBean propertyUtilsBean = BeanUtilsBean.getInstance().getPropertyUtils();
+
+    private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+    @Override
+    public boolean isSupport(DataAccessConfig access) {
+        return access instanceof FieldScopeDataAccessConfig;
+    }
+
+    @Override
+    public boolean handle(DataAccessConfig access, MethodInterceptorParamContext context) {
+        FieldScopeDataAccessConfig own = ((FieldScopeDataAccessConfig) access);
+        Object controller = context.getTarget();
+        if (controller != null) {
+            switch (access.getAction()) {
+                case Permission.ACTION_QUERY:
+                    return doQueryAccess(own, context);
+                case Permission.ACTION_GET:
+                case Permission.ACTION_DELETE:
+                case Permission.ACTION_UPDATE:
+                    return doRWAccess(own, context, controller);
+                case Permission.ACTION_ADD:
+                default:
+                    logger.warn("action: {} not support now!", access.getAction());
+            }
+        } else {
+            logger.warn("target is null!");
+        }
+        return true;
+    }
+
+    @SuppressWarnings("unchecked")
+    protected boolean doRWAccess(FieldScopeDataAccessConfig access, MethodInterceptorParamContext context, Object controller) {
+        //获取注解
+        RequiresDataAccess dataAccess = context.getAnnotation(RequiresDataAccess.class);
+        Object id = context.<String>getParameter(dataAccess.idParamName()).orElse(null);
+        //通过QueryController获取QueryService
+        //然后调用selectByPk 查询旧的数据,进行对比
+        if (controller instanceof QueryController) {
+            QueryService queryService = (QueryService) ((QueryController) controller).getService();
+            Object oldData = queryService.selectByPk(id);
+            if (oldData != null) {
+                try {
+                    Object value = propertyUtilsBean.getProperty(oldData, access.getField());
+                    return access.getScope().contains(value);
+                } catch (Exception e) {
+                    logger.error("can't read property {}", access.getField(), e);
+                }
+                return false;
+            }
+        } else {
+            logger.warn("controller is not instanceof QueryController");
+        }
+        return true;
+    }
+
+
+    protected boolean doQueryAccess(FieldScopeDataAccessConfig access, MethodInterceptorParamContext context) {
+        QueryParamEntity entity = context.getParams()
+                .values().stream()
+                .filter(QueryParamEntity.class::isInstance)
+                .map(QueryParamEntity.class::cast)
+                .findAny().orElse(null);
+        if (entity == null) {
+            logger.warn("try validate query access, but query entity is null or not instance of org.hswebframework.web.commons.entity.Entity");
+            return true;
+        }
+        //重构查询条件
+        //如: 旧的条件为 where column =? or column = ?
+        //重构后为: where creatorId=? and (column = ? or column = ?)
+        List<Term> oldParam = entity.getTerms();
+        //清空旧的查询条件
+        entity.setTerms(new ArrayList<>());
+        //添加一个查询条件
+        entity.addTerm(createQueryTerm(access))
+                //客户端提交的参数 作为嵌套参数
+                .nest().setTerms(oldParam);
+        return true;
+    }
+
+    protected Term createQueryTerm(FieldScopeDataAccessConfig access) {
+        Term term = new Term();
+        term.setType(Term.Type.and);
+        term.setColumn(access.getField());
+        term.setTermType(TermType.in);
+        term.setValue(access.getScope());
+        return term;
+    }
+}

+ 3 - 3
hsweb-authorization/hsweb-authorization-shiro/src/main/java/org/hswebframework/web/authorization/shiro/boost/handler/OwnCreatedDataAccessHandler.java

@@ -14,7 +14,7 @@ import org.hswebframework.web.commons.entity.RecordCreationEntity;
 import org.hswebframework.web.commons.entity.param.QueryParamEntity;
 import org.hswebframework.web.controller.QueryController;
 import org.hswebframework.web.service.QueryService;
-import org.hswebframwork.utils.ClassUtils;
+import org.hswebframework.utils.ClassUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -53,7 +53,7 @@ public class OwnCreatedDataAccessHandler implements DataAccessHandler {
                     logger.warn("action: {} not support now!", access.getAction());
             }
         } else {
-            logger.warn("target is not instance of HswebController!");
+            logger.warn("target is null!");
         }
         return true;
     }
@@ -116,7 +116,7 @@ public class OwnCreatedDataAccessHandler implements DataAccessHandler {
             queryParamEntity.setTerms(new ArrayList<>());
             //添加一个查询条件
             queryParamEntity
-                    .where(RecordCreationEntity.creatorId,Authentication.current().orElseThrow(AuthorizeException::new).getUser().getId())
+                    .where(RecordCreationEntity.creatorId, Authentication.current().orElseThrow(AuthorizeException::new).getUser().getId())
                     //客户端提交的参数 作为嵌套参数
                     .nest().setTerms(oldParam);
         } else if (entity instanceof RecordCreationEntity) {

+ 1 - 1
hsweb-authorization/hsweb-authorization-shiro/src/main/java/org/hswebframework/web/authorization/shiro/boost/handler/ScriptDataAccessHandler.java

@@ -6,7 +6,7 @@ import org.hswebframework.expands.script.engine.DynamicScriptEngineFactory;
 import org.hswebframework.web.BusinessException;
 import org.hswebframework.web.authorization.access.*;
 import org.hswebframework.web.boost.aop.context.MethodInterceptorParamContext;
-import org.hswebframwork.utils.StringUtils;
+import org.hswebframework.utils.StringUtils;
 
 /**
  * TODO 完成注释

+ 11 - 0
hsweb-boost/hsweb-boost-aop/src/main/java/org/hswebframework/web/boost/aop/context/MethodInterceptorHolder.java

@@ -36,6 +36,9 @@ import java.util.Optional;
  * @author zhouhao
  */
 public class MethodInterceptorHolder {
+    /**
+     * 参数名称获取器,用于获取方法参数的名称
+     */
     public static final ParameterNameDiscoverer nameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
 
     public static MethodInterceptorHolder current() {
@@ -104,6 +107,14 @@ public class MethodInterceptorHolder {
         return args;
     }
 
+    public <T extends Annotation> T findMethodAnnotation(Class<T> annClass) {
+        return AopUtils.findMethodAnnotation(annClass, method, annClass);
+    }
+
+    public <T extends Annotation> T findClassAnnotation(Class<T> annClass) {
+        return AopUtils.findAnnotation(target.getClass(), annClass);
+    }
+
     public <T extends Annotation> T findAnnotation(Class<T> annClass) {
         return AopUtils.findAnnotation(target.getClass(), method, annClass);
     }

+ 21 - 0
hsweb-boost/hsweb-boost-validator/hsweb-boost-validator-group/pom.xml

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>hsweb-boost-validator</artifactId>
+        <groupId>org.hswebframework.web</groupId>
+        <version>3.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>hsweb-boost-validator-group</artifactId>
+
+
+    <dependencies>
+        <dependency>
+            <groupId>org.hibernate</groupId>
+            <artifactId>hibernate-validator</artifactId>
+        </dependency>
+    </dependencies>
+</project>

+ 10 - 0
hsweb-boost/hsweb-boost-validator/hsweb-boost-validator-group/src/main/java/org/hswebframework/web/validator/group/CreateGroup.java

@@ -0,0 +1,10 @@
+package org.hswebframework.web.validator.group;
+
+/**
+ * 使用此Group,只在新增时验证数据
+ *
+ * @author zhouhao
+ * @since 3.0
+ */
+public interface CreateGroup {
+}

+ 10 - 0
hsweb-boost/hsweb-boost-validator/hsweb-boost-validator-group/src/main/java/org/hswebframework/web/validator/group/UpdateGroup.java

@@ -0,0 +1,10 @@
+package org.hswebframework.web.validator.group;
+
+/**
+ * 使用此group,只在修改的时候才进行验证
+ *
+ * @author zhouhao
+ * @since 3.0
+ */
+public interface UpdateGroup {
+}

+ 1 - 0
hsweb-boost/hsweb-boost-validator/pom.xml

@@ -31,6 +31,7 @@
     <packaging>pom</packaging>
     <modules>
         <module>hsweb-boost-validator-api</module>
+        <module>hsweb-boost-validator-group</module>
     </modules>
 
 

+ 1 - 1
hsweb-commons/hsweb-commons-controller/pom.xml

@@ -45,7 +45,7 @@
         </dependency>
         <dependency>
             <groupId>org.hswebframework.web</groupId>
-            <artifactId>hsweb-logging</artifactId>
+            <artifactId>hsweb-access-logging-api</artifactId>
             <version>${project.version}</version>
         </dependency>
         <dependency>

+ 2 - 0
hsweb-commons/hsweb-commons-controller/src/main/java/org/hswebframework/web/controller/CreateController.java

@@ -26,7 +26,9 @@ import org.hswebframework.web.controller.message.ResponseMessage;
 import org.hswebframework.web.logging.AccessLogger;
 import org.hswebframework.web.service.CreateEntityService;
 import org.hswebframework.web.service.InsertService;
+import org.hswebframework.web.validator.group.CreateGroup;
 import org.springframework.http.HttpStatus;
+import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.ResponseStatus;

+ 0 - 26
hsweb-commons/hsweb-commons-controller/src/main/java/org/hswebframework/web/controller/GenericEntityController.java

@@ -18,19 +18,9 @@
 
 package org.hswebframework.web.controller;
 
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiResponse;
-import io.swagger.annotations.ApiResponses;
-import org.hswebframework.web.authorization.Permission;
-import org.hswebframework.web.authorization.annotation.Authorize;
 import org.hswebframework.web.commons.entity.Entity;
 import org.hswebframework.web.commons.entity.GenericEntity;
-import org.hswebframework.web.controller.message.ResponseMessage;
-import org.hswebframework.web.logging.AccessLogger;
 import org.hswebframework.web.service.CrudService;
-import org.springframework.web.bind.annotation.PatchMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.RequestBody;
 
 /**
  * 通用实体的增删改查控制器
@@ -45,20 +35,4 @@ public interface GenericEntityController<E extends GenericEntity<PK>, PK, Q exte
 
     CrudService<E, PK> getService();
 
-    @Authorize(action = {Permission.ACTION_UPDATE, Permission.ACTION_ADD})
-    @PatchMapping(path = "/{id}")
-    @AccessLogger("{save_or_update}")
-    @ApiOperation("根据ID修改数据,如果数据不存在则新增一条数据")
-    @ApiResponses({
-            @ApiResponse(code = 200, message = "修改(新增)成功,返回数据ID"),
-            @ApiResponse(code = 401, message = "未授权"),
-            @ApiResponse(code = 403, message = "无权限"),
-            @ApiResponse(code = 409, message = "存在重复的资源")
-    })
-    default ResponseMessage<PK> saveOrUpdate(@PathVariable PK id, @RequestBody M data) {
-        E entity = getService().createEntity();
-        entity.setId(id);
-        return ResponseMessage.ok(getService().saveOrUpdate(modelToEntity(data, entity)));
-    }
-
 }

+ 0 - 24
hsweb-commons/hsweb-commons-controller/src/main/java/org/hswebframework/web/controller/SimpleGenericEntityController.java

@@ -18,19 +18,9 @@
 
 package org.hswebframework.web.controller;
 
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiResponse;
-import io.swagger.annotations.ApiResponses;
-import org.hswebframework.web.authorization.Permission;
-import org.hswebframework.web.authorization.annotation.Authorize;
 import org.hswebframework.web.commons.entity.Entity;
 import org.hswebframework.web.commons.entity.GenericEntity;
-import org.hswebframework.web.controller.message.ResponseMessage;
-import org.hswebframework.web.logging.AccessLogger;
 import org.hswebframework.web.service.CrudService;
-import org.springframework.web.bind.annotation.PatchMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.RequestBody;
 
 /**
  * 通用实体的增删改查控制器
@@ -45,19 +35,5 @@ public interface SimpleGenericEntityController<E extends GenericEntity<PK>, PK,
 
     CrudService<E, PK> getService();
 
-    @Authorize(action = {Permission.ACTION_UPDATE, Permission.ACTION_ADD})
-    @PatchMapping(path = "/{id}")
-    @AccessLogger("{save_or_update}")
-    @ApiOperation("根据ID修改数据,如果数据不存在则新增一条数据")
-    @ApiResponses({
-            @ApiResponse(code = 200, message = "修改(新增)成功,返回数据ID"),
-            @ApiResponse(code = 401, message = "未授权"),
-            @ApiResponse(code = 403, message = "无权限"),
-            @ApiResponse(code = 409, message = "存在重复的资源")
-    })
-    default ResponseMessage<PK> saveOrUpdate(@PathVariable PK id, @RequestBody E data) {
-        data.setId(id);
-        return ResponseMessage.ok(getService().saveOrUpdate(data));
-    }
 
 }

+ 13 - 8
hsweb-commons/hsweb-commons-controller/src/main/java/org/hswebframework/web/controller/UpdateController.java

@@ -23,10 +23,12 @@ import io.swagger.annotations.ApiResponse;
 import io.swagger.annotations.ApiResponses;
 import org.hswebframework.web.authorization.Permission;
 import org.hswebframework.web.authorization.annotation.Authorize;
+import org.hswebframework.web.authorization.annotation.Logical;
 import org.hswebframework.web.controller.message.ResponseMessage;
 import org.hswebframework.web.logging.AccessLogger;
 import org.hswebframework.web.service.CreateEntityService;
 import org.hswebframework.web.service.UpdateService;
+import org.springframework.web.bind.annotation.PatchMapping;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.PutMapping;
 import org.springframework.web.bind.annotation.RequestBody;
@@ -43,18 +45,21 @@ public interface UpdateController<E, PK, M> {
     @PutMapping(path = "/{id}")
     @AccessLogger("{update_by_primary_key}")
     @ApiOperation("根据ID修改数据")
-    @ApiResponses({
-            @ApiResponse(code = 200, message = "修改成功"),
-            @ApiResponse(code = 401, message = "未授权"),
-            @ApiResponse(code = 403, message = "无权限"),
-            @ApiResponse(code = 404, message = "要修改的数据不存在"),
-            @ApiResponse(code = 409, message = "存在重复的资源")
-    })
-    default ResponseMessage updateByPrimaryKey(@PathVariable PK id, @RequestBody M data) {
+    default ResponseMessage<Integer> updateByPrimaryKey(@PathVariable PK id, @RequestBody M data) {
         E entity = getService().createEntity();
         return ResponseMessage.ok(getService().updateByPk(id, modelToEntity(data, entity)));
     }
 
+    @Authorize(action = {Permission.ACTION_UPDATE, Permission.ACTION_ADD}, logical = Logical.AND)
+    @PatchMapping
+    @AccessLogger("{save_or_update}")
+    @ApiOperation("保存数据,如果数据不存在则新增一条数据")
+    default ResponseMessage<PK> saveOrUpdate(@RequestBody M data) {
+        E entity = getService().createEntity();
+        return ResponseMessage.ok(getService().saveOrUpdate(modelToEntity(data, entity)));
+    }
+
+
     /**
      * 将model转为entity
      *

+ 1 - 1
hsweb-commons/hsweb-commons-dao/hsweb-commons-dao-api/src/main/java/org/hswebframework/web/dao/datasource/DatabaseType.java

@@ -19,7 +19,7 @@
 package org.hswebframework.web.dao.datasource;
 
 import org.hsweb.ezorm.rdb.render.dialect.Dialect;
-import org.hswebframwork.utils.StringUtils;
+import org.hswebframework.utils.StringUtils;
 
 public enum DatabaseType {
     unknown(null, null, null, null),

+ 53 - 0
hsweb-commons/hsweb-commons-dao/hsweb-commons-dao-mybatis/src/main/java/org/hswebframework/web/dao/mybatis/DefaultJdbcExecutor.java

@@ -0,0 +1,53 @@
+package org.hswebframework.web.dao.mybatis;
+
+import org.hsweb.ezorm.rdb.executor.AbstractJdbcSqlExecutor;
+import org.hsweb.ezorm.rdb.executor.SQL;
+import org.springframework.jdbc.datasource.DataSourceUtils;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.sql.DataSource;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.Objects;
+
+/**
+ * @author zhouhao
+ */
+@Transactional(rollbackFor = Throwable.class)
+public class DefaultJdbcExecutor extends AbstractJdbcSqlExecutor {
+    private DataSource dataSource;
+
+    public DefaultJdbcExecutor(DataSource dataSource) {
+        Objects.requireNonNull(dataSource);
+        this.dataSource = dataSource;
+    }
+
+    @Override
+    public Connection getConnection() {
+        return DataSourceUtils.getConnection(dataSource);
+    }
+
+    @Override
+    public void releaseConnection(Connection connection) throws SQLException {
+        DataSourceUtils.releaseConnection(connection, dataSource);
+    }
+    
+    @Override
+    @Transactional(propagation = Propagation.NOT_SUPPORTED)
+    public void exec(SQL sql) throws SQLException {
+        super.exec(sql);
+    }
+
+    @Override
+    @Transactional(propagation = Propagation.NOT_SUPPORTED)
+    public void exec(String sql) throws SQLException {
+        super.exec(sql);
+    }
+
+    @Override
+    @Transactional(propagation = Propagation.NOT_SUPPORTED)
+    public void exec(String sql, Object params) throws SQLException {
+        super.exec(sql, params);
+    }
+}

+ 25 - 1
hsweb-commons/hsweb-commons-dao/hsweb-commons-dao-mybatis/src/main/java/org/hswebframework/web/dao/mybatis/MyBatisAutoConfiguration.java

@@ -23,15 +23,20 @@ import org.apache.ibatis.plugin.Interceptor;
 import org.apache.ibatis.session.SqlSessionFactory;
 import org.apache.ibatis.session.TransactionIsolationLevel;
 import org.apache.ibatis.transaction.Transaction;
+import org.hsweb.ezorm.rdb.executor.AbstractJdbcSqlExecutor;
+import org.hsweb.ezorm.rdb.executor.SqlExecutor;
+import org.hswebframework.web.commons.entity.factory.EntityFactory;
+import org.hswebframework.web.dao.datasource.DataSourceHolder;
+import org.hswebframework.web.dao.datasource.DatabaseType;
 import org.hswebframework.web.dao.mybatis.dynamic.DynamicDataSourceSqlSessionFactoryBuilder;
 import org.hswebframework.web.dao.mybatis.dynamic.DynamicSpringManagedTransaction;
 import org.mybatis.spring.SqlSessionFactoryBean;
-import org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration;
 import org.mybatis.spring.boot.autoconfigure.SpringBootVFS;
 import org.mybatis.spring.transaction.SpringManagedTransactionFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 import org.springframework.boot.context.properties.ConfigurationProperties;
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.context.annotation.Bean;
@@ -39,10 +44,13 @@ import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Primary;
 import org.springframework.core.io.DefaultResourceLoader;
 import org.springframework.core.io.ResourceLoader;
+import org.springframework.jdbc.datasource.DataSourceUtils;
 import org.springframework.util.StringUtils;
 
 import javax.annotation.Resource;
 import javax.sql.DataSource;
+import java.sql.Connection;
+import java.sql.SQLException;
 
 @Configuration
 @EnableConfigurationProperties(MybatisProperties.class)
@@ -61,6 +69,9 @@ public class MyBatisAutoConfiguration {
     @Autowired(required = false)
     private DatabaseIdProvider databaseIdProvider;
 
+    @Autowired(required = false)
+    private EntityFactory entityFactory;
+
     @Bean
     @Primary
     @ConfigurationProperties(prefix = MybatisProperties.MYBATIS_PREFIX)
@@ -71,6 +82,9 @@ public class MyBatisAutoConfiguration {
     @Bean(name = "sqlSessionFactory")
     public SqlSessionFactory sqlSessionFactory(@Qualifier("dataSource") DataSource dataSource) throws Exception {
         SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
+        if (null != entityFactory) {
+            factory.setObjectFactory(new MybatisEntityFactory(entityFactory));
+        }
         factory.setVfs(SpringBootVFS.class);
         if (mybatisProperties.isDynamicDatasource()) {
             factory.setSqlSessionFactoryBuilder(new DynamicDataSourceSqlSessionFactoryBuilder());
@@ -102,4 +116,14 @@ public class MyBatisAutoConfiguration {
         return factory.getObject();
     }
 
+    @Bean
+    @ConditionalOnMissingBean(SqlExecutor.class)
+    public SqlExecutor sqlExecutor(DataSource dataSource) {
+        try (Connection connection = dataSource.getConnection()) {
+            DataSourceHolder.install(dataSource, DatabaseType.fromJdbcUrl(connection.getMetaData().getURL()));
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+        return new DefaultJdbcExecutor(dataSource);
+    }
 }

+ 38 - 0
hsweb-commons/hsweb-commons-dao/hsweb-commons-dao-mybatis/src/main/java/org/hswebframework/web/dao/mybatis/MybatisEntityFactory.java

@@ -0,0 +1,38 @@
+package org.hswebframework.web.dao.mybatis;
+
+import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
+import org.hswebframework.web.commons.entity.factory.EntityFactory;
+
+import java.util.*;
+
+/**
+ * TODO 完成注释
+ *
+ * @author zhouhao
+ */
+public class MybatisEntityFactory extends DefaultObjectFactory {
+
+    private EntityFactory entityFactory;
+
+    public MybatisEntityFactory(EntityFactory entityFactory) {
+        this.entityFactory = entityFactory;
+    }
+
+    @Override
+    protected Class<?> resolveInterface(Class<?> type) {
+        Class<?> classToCreate;
+        if (type == List.class || type == Collection.class || type == Iterable.class) {
+            classToCreate = ArrayList.class;
+        } else if (type == Map.class) {
+            classToCreate = HashMap.class;
+        } else if (type == SortedSet.class) { // issue #510 Collections Support
+            classToCreate = TreeSet.class;
+        } else if (type == Set.class) {
+            classToCreate = HashSet.class;
+        } else {
+            // entity interface
+            classToCreate = entityFactory.getInstanceType(type);
+        }
+        return classToCreate;
+    }
+}

+ 13 - 0
hsweb-commons/hsweb-commons-dao/hsweb-commons-dao-mybatis/src/main/java/org/hswebframework/web/dao/mybatis/MybatisMapperCustomer.java

@@ -0,0 +1,13 @@
+package org.hswebframework.web.dao.mybatis;
+
+/**
+ * 排除不需要加载的mapper.xml
+ *
+ * @author zhouhao
+ * @since 3.0
+ */
+public interface MybatisMapperCustomer {
+    String[] getExcludes();
+
+    String[] getIncludes();
+}

+ 36 - 8
hsweb-commons/hsweb-commons-dao/hsweb-commons-dao-mybatis/src/main/java/org/hswebframework/web/dao/mybatis/MybatisProperties.java

@@ -18,6 +18,7 @@
 
 package org.hswebframework.web.dao.mybatis;
 
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.core.io.Resource;
 import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
 
@@ -55,6 +56,13 @@ public class MybatisProperties extends org.mybatis.spring.boot.autoconfigure.Myb
      */
     private              String[] mapperLocationExcludes = null;
 
+    private List<MybatisMapperCustomer> mybatisMappers;
+
+    @Autowired(required = false)
+    public void setMybatisMappers(List<MybatisMapperCustomer> mybatisMappers) {
+        this.mybatisMappers = mybatisMappers;
+    }
+
     public String[] getMapperLocationExcludes() {
         return mapperLocationExcludes;
     }
@@ -79,7 +87,16 @@ public class MybatisProperties extends org.mybatis.spring.boot.autoconfigure.Myb
             locations = new HashSet<>();
         else
             locations = Arrays.stream(getMapperLocations()).collect(Collectors.toSet());
+
         locations.add(defaultMapperLocation);
+
+        if (mybatisMappers != null) {
+            mybatisMappers.stream()
+                    .map(MybatisMapperCustomer::getIncludes)
+                    .flatMap(Arrays::stream)
+                    .forEach(locations::add);
+        }
+
         for (String mapperLocation : locations) {
             Resource[] mappers;
             try {
@@ -90,16 +107,27 @@ public class MybatisProperties extends org.mybatis.spring.boot.autoconfigure.Myb
             } catch (IOException e) {
             }
         }
-        //排除不需要的配置
+        Set<String> excludes = new HashSet<>();
+        if (mybatisMappers != null) {
+            mybatisMappers.stream()
+                    .map(MybatisMapperCustomer::getExcludes)
+                    .flatMap(Arrays::stream)
+                    .forEach(excludes::add);
+        }
         if (mapperLocationExcludes != null && mapperLocationExcludes.length > 0) {
-            for (String mapperLocationExclude : mapperLocationExcludes) {
-                try {
-                    Resource[] excludesMappers = new PathMatchingResourcePatternResolver().getResources(mapperLocationExclude);
-                    for (Resource excludesMapper : excludesMappers) {
-                        resources.remove(excludesMapper.getURL().toString());
-                    }
-                } catch (IOException e) {
+            for (String exclude : mapperLocationExcludes) {
+                excludes.add(exclude);
+            }
+        }
+        PathMatchingResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
+        //排除不需要的配置
+        for (String mapperLocationExclude : excludes) {
+            try {
+                Resource[] excludesMappers = resourcePatternResolver.getResources(mapperLocationExclude);
+                for (Resource excludesMapper : excludesMappers) {
+                    resources.remove(excludesMapper.getURL().toString());
                 }
+            } catch (IOException e) {
             }
         }
         Resource[] mapperLocations = new Resource[resources.size()];

+ 10 - 2
hsweb-commons/hsweb-commons-dao/hsweb-commons-dao-mybatis/src/main/java/org/hswebframework/web/dao/mybatis/builder/EasyOrmSqlBuilder.java

@@ -29,6 +29,8 @@ import org.hsweb.ezorm.core.param.UpdateParam;
 import org.hsweb.ezorm.rdb.meta.RDBColumnMetaData;
 import org.hsweb.ezorm.rdb.meta.RDBDatabaseMetaData;
 import org.hsweb.ezorm.rdb.meta.RDBTableMetaData;
+import org.hsweb.ezorm.rdb.meta.converter.DateTimeConverter;
+import org.hsweb.ezorm.rdb.meta.converter.NumberValueConverter;
 import org.hsweb.ezorm.rdb.render.SqlAppender;
 import org.hsweb.ezorm.rdb.render.SqlRender;
 import org.hsweb.ezorm.rdb.render.dialect.Dialect;
@@ -42,7 +44,7 @@ import org.hswebframework.web.dao.datasource.DataSourceHolder;
 import org.hswebframework.web.dao.datasource.DatabaseType;
 import org.hswebframework.web.dao.mybatis.plgins.pager.Pager;
 import org.hswebframework.web.dao.mybatis.utils.ResultMapsUtils;
-import org.hswebframwork.utils.StringUtils;
+import org.hswebframework.utils.StringUtils;
 
 import java.sql.JDBCType;
 import java.util.*;
@@ -146,6 +148,12 @@ public class EasyOrmSqlBuilder {
                     column.setAlias(resultMapping.getProperty());
                 column.setJavaType(resultMapping.getJavaType());
                 column.setProperty("resultMapping", resultMapping);
+                if (column.getJdbcType() == JDBCType.DATE || column.getJdbcType() == JDBCType.TIME) {
+                    column.setValueConverter(new DateTimeConverter("yyyy-MM-dd HH:mm:ss", column.getJavaType()));
+                }
+                if (column.getJdbcType() == JDBCType.NUMERIC) {
+                    column.setValueConverter(new NumberValueConverter(column.getJavaType()));
+                }
                 rdbTableMetaData.addColumn(column);
             }
         });
@@ -248,7 +256,7 @@ public class EasyOrmSqlBuilder {
         }
         RDBTableMetaData tableMetaData = createMeta(tableName, resultMapId);
         SqlAppender appender = new SqlAppender(" order by ");
-        param.getSorts().stream()
+        param.getSorts()
                 .forEach(sort -> {
                     RDBColumnMetaData column = tableMetaData.getColumn(sort.getName());
                     if (column == null)

+ 5 - 0
hsweb-commons/hsweb-commons-entity/pom.xml

@@ -40,6 +40,11 @@
             <artifactId>hsweb-easy-orm-rdb</artifactId>
             <optional>true</optional>
         </dependency>
+        <dependency>
+            <groupId>org.hswebframework.web</groupId>
+            <artifactId>hsweb-boost-validator-group</artifactId>
+            <version>${project.version}</version>
+        </dependency>
         <dependency>
             <groupId>org.hswebframework</groupId>
             <artifactId>hsweb-utils</artifactId>

+ 12 - 0
hsweb-commons/hsweb-commons-entity/src/main/java/org/hswebframework/web/commons/entity/DataStatus.java

@@ -0,0 +1,12 @@
+package org.hswebframework.web.commons.entity;
+
+/**
+ * TODO 完成注释
+ *
+ * @author zhouhao
+ */
+public interface DataStatus {
+    Byte STATUS_ENABLED  = 1;
+    Byte STATUS_DISABLED = 0;
+    Byte STATUS_LOCKED   = -1;
+}

+ 2 - 1
hsweb-commons/hsweb-commons-entity/src/main/java/org/hswebframework/web/commons/entity/SortSupportEntity.java

@@ -28,6 +28,7 @@ public interface SortSupportEntity extends Comparable<SortSupportEntity>, Entity
 
     default int compareTo(SortSupportEntity support) {
         if (support == null) return -1;
-        return Long.compare(getSortIndex(), support.getSortIndex());
+        
+        return Long.compare(getSortIndex() == null ? 0 : getSortIndex(), support.getSortIndex() == null ? 0 : support.getSortIndex());
     }
 }

+ 80 - 33
hsweb-commons/hsweb-commons-entity/src/main/java/org/hswebframework/web/commons/entity/TreeSupportEntity.java

@@ -20,14 +20,13 @@ package org.hswebframework.web.commons.entity;
 
 
 import org.hswebframework.web.id.IDGenerator;
-import org.hswebframwork.utils.RandomUtil;
-import org.hswebframwork.utils.StringUtils;
+import org.hswebframework.utils.RandomUtil;
 
+import java.math.BigDecimal;
 import java.util.*;
-import java.util.function.BiConsumer;
-import java.util.function.Function;
-import java.util.function.Predicate;
+import java.util.function.*;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 public interface TreeSupportEntity<PK> extends GenericEntity<PK> {
 
@@ -49,11 +48,6 @@ public interface TreeSupportEntity<PK> extends GenericEntity<PK> {
 
     void setLevel(Integer level);
 
-    default void setLevelFromPath() {
-        if (getPath() != null)
-            setLevel(getPath().split("-").length);
-    }
-
     <T extends TreeSupportEntity<PK>> List<T> getChildren();
 
     /**
@@ -67,6 +61,15 @@ public interface TreeSupportEntity<PK> extends GenericEntity<PK> {
         return path.substring(0, path.length() - 5);
     }
 
+    static <T extends TreeSupportEntity> void forEach(Collection<T> list, Consumer<T> consumer) {
+        list.forEach(node -> {
+            consumer.accept(node);
+            if (node.getChildren() != null) {
+                forEach(node.getChildren(), consumer);
+            }
+        });
+    }
+
     /**
      * 将树形结构转为列表结构,并填充对应的数据。<br>
      * 如树结构数据: {name:'父节点',children:[{name:'子节点1'},{name:'子节点2'}]}<br>
@@ -82,7 +85,14 @@ public interface TreeSupportEntity<PK> extends GenericEntity<PK> {
         List<T> children = parent.getChildren();
         if (parent.getPath() == null) {
             parent.setPath(RandomUtil.randomChar(4));
-            parent.setLevelFromPath();
+            if (parent.getPath() != null)
+                parent.setLevel(parent.getPath().split("-").length);
+            if (parent instanceof SortSupportEntity) {
+                Long index = ((SortSupportEntity) parent).getSortIndex();
+                if (null == index) {
+                    ((SortSupportEntity) parent).setSortIndex(1L);
+                }
+            }
         }
         if (children != null) {
             PK pid = parent.getId();
@@ -93,11 +103,15 @@ public interface TreeSupportEntity<PK> extends GenericEntity<PK> {
             for (int i = 0; i < children.size(); i++) {
                 T child = children.get(i);
                 if (child instanceof SortSupportEntity && parent instanceof SortSupportEntity) {
-                    ((SortSupportEntity) child).setSortIndex(StringUtils.toLong(((SortSupportEntity) parent).getSortIndex() + "0" + (i + 1)));
+                    Long index = ((SortSupportEntity) parent).getSortIndex();
+                    if (null == index) {
+                        ((SortSupportEntity) parent).setSortIndex(index = 1L);
+                    }
+                    ((SortSupportEntity) child).setSortIndex(new BigDecimal(index + "0" + (i + 1)).longValue());
                 }
                 child.setParentId(pid);
                 child.setPath(parent.getPath() + "-" + RandomUtil.randomChar(4));
-                child.setLevelFromPath();
+                child.setLevel(child.getPath().split("-").length);
                 target.add(child);
                 expandTree2List(child, target, idGenerator);
             }
@@ -108,54 +122,87 @@ public interface TreeSupportEntity<PK> extends GenericEntity<PK> {
      * 集合转为树形结构,返回根节点集合
      *
      * @param dataList      需要转换的集合
-     * @param childAccepter 设置子节点回调
-     * @param <T>           树节点类型
+     * @param childConsumer 设置子节点回调
+     * @param <N>           树节点类型
      * @param <PK>          主键类型
      * @return 树形结构集合
      */
-    static <T extends TreeSupportEntity<PK>, PK> List<T> list2tree(Collection<T> dataList, BiConsumer<T, List<T>> childAccepter) {
-        return list2tree(dataList, childAccepter, (Function<RootNodePredicate<T, PK>, Predicate<T>>) predicate -> node -> node == null || predicate.getNode(node.getParentId()) == null);
+    static <N extends TreeSupportEntity<PK>, PK> List<N> list2tree(Collection<N> dataList, BiConsumer<N, List<N>> childConsumer) {
+        return list2tree(dataList, childConsumer, (Function<TreeHelper<N, PK>, Predicate<N>>) predicate -> node -> node == null || predicate.getNode(node.getParentId()) == null);
     }
 
-    static <T extends TreeSupportEntity<PK>, PK> List<T> list2tree(Collection<T> dataList,
-                                                                   BiConsumer<T, List<T>> childAccepter,
-                                                                   Predicate<T> rootNodePredicate) {
-        return list2tree(dataList, childAccepter, (Function<RootNodePredicate<T, PK>, Predicate<T>>) predicate -> rootNodePredicate);
+    static <N extends TreeSupportEntity<PK>, PK> List<N> list2tree(Collection<N> dataList,
+                                                                   BiConsumer<N, List<N>> childConsumer,
+                                                                   Predicate<N> rootNodePredicate) {
+        return list2tree(dataList, childConsumer, (Function<TreeHelper<N, PK>, Predicate<N>>) predicate -> rootNodePredicate);
     }
 
-    static <T extends TreeSupportEntity<PK>, PK> List<T> list2tree(Collection<T> dataList,
-                                                                   BiConsumer<T, List<T>> childAccepter,
-                                                                   Function<RootNodePredicate<T, PK>, Predicate<T>> predicateFunction) {
-        // id,obj
-        Map<PK, T> cache = new HashMap<>();
+    /**
+     * 列表结构转为树结构,并返回根节点集合
+     *
+     * @param dataList          数据集合
+     * @param childConsumer     子节点消费接口,用于设置子节点
+     * @param predicateFunction 根节点判断函数,传入helper,获取一个判断是否为跟节点的函数
+     * @param <N>               元素类型
+     * @param <PK>              主键类型
+     * @return 根节点集合
+     */
+    static <N extends TreeSupportEntity<PK>, PK> List<N> list2tree(final Collection<N> dataList,
+                                                                   final BiConsumer<N, List<N>> childConsumer,
+                                                                   final Function<TreeHelper<N, PK>, Predicate<N>> predicateFunction) {
+        Objects.requireNonNull(dataList, "source list can not be null");
+        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() < 1000 ? dataList.stream() : dataList.parallelStream();
+        // id,node
+        Map<PK, N> cache = new HashMap<>();
         // parentId,children
-        Map<PK, List<T>> treeCache = dataList.parallelStream()
+        Map<PK, List<N>> treeCache = streamSupplier.get()
                 .peek(node -> cache.put(node.getId(), node))
                 .collect(Collectors.groupingBy(TreeSupportEntity::getParentId));
 
-        Predicate<T> rootNodePredicate = predicateFunction.apply(new RootNodePredicate<T, PK>() {
+        Predicate<N> rootNodePredicate = predicateFunction.apply(new TreeHelper<N, PK>() {
             @Override
-            public List<T> getChildren(PK parentId) {
+            public List<N> getChildren(PK parentId) {
                 return treeCache.get(parentId);
             }
 
             @Override
-            public T getNode(PK id) {
+            public N getNode(PK id) {
                 return cache.get(id);
             }
         });
 
-        return dataList.parallelStream()
+        return streamSupplier.get()
                 //设置每个节点的子节点
-                .peek(node -> childAccepter.accept(node, treeCache.get(node.getId())))
+                .peek(node -> childConsumer.accept(node, treeCache.get(node.getId())))
                 //获取根节点
                 .filter(rootNodePredicate)
                 .collect(Collectors.toList());
     }
 
-    interface RootNodePredicate<T, PK> {
+    /**
+     * 树结构Helper
+     *
+     * @param <T>  节点类型
+     * @param <PK> 主键类型
+     */
+    interface TreeHelper<T, PK> {
+        /**
+         * 根据主键获取子节点
+         *
+         * @param parentId 节点ID
+         * @return 子节点集合
+         */
         List<T> getChildren(PK parentId);
 
+        /**
+         * 根据id获取节点
+         *
+         * @param id 节点ID
+         * @return 节点
+         */
         T getNode(PK id);
     }
 }

+ 71 - 32
hsweb-commons/hsweb-commons-entity/src/main/java/org/hswebframework/web/commons/entity/factory/MapperEntityFactory.java

@@ -18,23 +18,25 @@
 
 package org.hswebframework.web.commons.entity.factory;
 
-import org.apache.commons.beanutils.BeanUtils;
+import com.alibaba.fastjson.JSON;
 import org.hswebframework.web.NotFoundException;
+import org.hswebframework.utils.ClassUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.lang.reflect.Modifier;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.*;
 import java.util.function.Supplier;
 
 /**
  * @author zhouhao
  * @since 3.0
  */
+@SuppressWarnings("unchecked")
 public class MapperEntityFactory implements EntityFactory {
-    private Map<Class, Mapper> realTypeMapper = new HashMap<>();
-    private Logger             logger         = LoggerFactory.getLogger(this.getClass());
+    private Map<Class, Mapper>          realTypeMapper = new HashMap<>();
+    private Logger                      logger         = LoggerFactory.getLogger(this.getClass());
+    private Map<String, PropertyCopier> copierCache    = new HashMap<>();
 
     public MapperEntityFactory() {
     }
@@ -43,50 +45,84 @@ public class MapperEntityFactory implements EntityFactory {
         this.realTypeMapper.putAll(realTypeMapper);
     }
 
-    public <T> MapperEntityFactory addMapping(Class<T> target, Mapper<T> mapper) {
+    public <T> MapperEntityFactory addMapping(Class<T> target, Mapper<? extends T> mapper) {
         realTypeMapper.put(target, mapper);
         return this;
     }
 
+    public MapperEntityFactory addCopier(PropertyCopier copier) {
+        Class source = ClassUtils.getGenericType(copier.getClass(), 0);
+        Class target = ClassUtils.getGenericType(copier.getClass(), 1);
+        if (source == null || source == Object.class) {
+            throw new UnsupportedOperationException("generic type " + source + " not support");
+        }
+        if (target == null || target == Object.class) {
+            throw new UnsupportedOperationException("generic type " + target + " not support");
+        }
+        addCopier(source, target, copier);
+        return this;
+    }
+
+    public <S, T> MapperEntityFactory addCopier(Class<S> source, Class<T> target, PropertyCopier<S, T> copier) {
+        copierCache.put(getCopierCacheKey(source, target), copier);
+        return this;
+    }
+
+    private String getCopierCacheKey(Class source, Class target) {
+        return source.getName().concat("->").concat(target.getName());
+    }
+
     @Override
     public <S, T> T copyProperties(S source, T target) {
+        Objects.requireNonNull(source);
+        Objects.requireNonNull(target);
         try {
-            // TODO: 17-3-30 应该设计为可自定义
-            BeanUtils.copyProperties(target, source);
+            PropertyCopier<S, T> copier = copierCache.<S, T>get(getCopierCacheKey(source.getClass(), target.getClass()));
+            if (null != copier) return copier.copyProperties(source, target);
+
+            return JSON.parseObject(JSON.toJSONString(source), (Class<T>) target.getClass());
         } catch (Exception e) {
             logger.warn("copy properties error", e);
         }
         return target;
     }
 
+    protected <T> Mapper<T> initCache(Class<T> beanClass) {
+        Mapper<T> mapper = null;
+        Class<T> realType = null;
+        ServiceLoader<T> serviceLoader = ServiceLoader.load(beanClass, this.getClass().getClassLoader());
+        Iterator<T> iterator = serviceLoader.iterator();
+        if (iterator.hasNext()) {
+            realType = (Class<T>) iterator.next().getClass();
+        }
+        //尝试使用 Simple类,如: package.SimpleUserBean
+        if (realType == null) {
+            String simpleClassName = beanClass.getPackage().getName().concat(".Simple").concat(beanClass.getSimpleName());
+            try {
+                realType = (Class<T>) Class.forName(simpleClassName);
+            } catch (ClassNotFoundException e) {
+                // throw new NotFoundException(e.getMessage());
+            }
+        }
+        if (!Modifier.isInterface(beanClass.getModifiers()) && !Modifier.isAbstract(beanClass.getModifiers())) {
+            realType = beanClass;
+        }
+        if (realType != null) {
+            logger.debug("use instance {} for {}", realType, beanClass);
+            mapper = new Mapper<>(realType, new DefaultInstanceGetter(realType));
+            realTypeMapper.put(beanClass, mapper);
+        }
+        return mapper;
+    }
+
     @Override
-    @SuppressWarnings("unchecked")
     public <T> T newInstance(Class<T> beanClass) {
         if (beanClass == null) return null;
         Mapper<T> mapper = realTypeMapper.get(beanClass);
         if (mapper != null) return mapper.getInstanceGetter().get();
-        synchronized (beanClass) {
-            mapper = realTypeMapper.get(beanClass);
-            if (mapper != null) return mapper.getInstanceGetter().get();
-            Class<T> realType = null;
-            if (!Modifier.isInterface(beanClass.getModifiers()) && !Modifier.isAbstract(beanClass.getModifiers())) {
-                realType = beanClass;
-            }
-            //尝试使用 Simple类,如: package.SimpleUserBean
-            if (realType == null) {
-                String simpleClassName = beanClass.getPackage().getName().concat(".Simple").concat(beanClass.getSimpleName());
-                try {
-                    realType = (Class<T>) Class.forName(simpleClassName);
-                } catch (ClassNotFoundException e) {
-                    throw new NotFoundException(e.getMessage());
-                }
-            }
-            if (realType != null) {
-                mapper = new Mapper<>(realType, new DefaultInstanceGetter(realType));
-                realTypeMapper.put(beanClass, mapper);
-                return mapper.getInstanceGetter().get();
-            }
-        }
+        mapper = initCache(beanClass);
+        if (mapper != null) return mapper.getInstanceGetter().get();
+
         throw new NotFoundException("can't create instance for " + beanClass);
     }
 
@@ -97,7 +133,10 @@ public class MapperEntityFactory implements EntityFactory {
         if (null != mapper) {
             return mapper.getTarget();
         }
-        return null;
+        mapper = initCache(beanClass);
+        if (mapper != null)
+            return mapper.getTarget();
+        return beanClass;
     }
 
     public static class Mapper<T> {

+ 11 - 0
hsweb-commons/hsweb-commons-entity/src/main/java/org/hswebframework/web/commons/entity/factory/PropertyCopier.java

@@ -0,0 +1,11 @@
+package org.hswebframework.web.commons.entity.factory;
+
+/**
+ * 属性复制接口,用于自定义属性复制
+ *
+ * @author zhouhao
+ * @since 3.0
+ */
+public interface PropertyCopier<S, T> {
+    T copyProperties(S source, T target);
+}

+ 19 - 3
hsweb-commons/hsweb-commons-service/hsweb-commons-service-simple/src/main/java/org/hswebframework/web/service/AbstractService.java

@@ -1,16 +1,19 @@
 package org.hswebframework.web.service;
 
+import org.hswebframework.utils.ClassUtils;
 import org.hswebframework.web.NotFoundException;
 import org.hswebframework.web.commons.entity.Entity;
 import org.hswebframework.web.commons.entity.factory.EntityFactory;
 import org.hswebframework.web.validate.SimpleValidateResults;
 import org.hswebframework.web.validate.ValidationException;
-import org.hswebframwork.utils.ClassUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 
+import javax.validation.ConstraintViolation;
 import javax.validation.Validator;
+import java.util.Set;
+import java.util.function.Supplier;
 
 /**
  * 抽象服务类,提供通用模板方法、类,如验证器,实体工厂等
@@ -95,13 +98,26 @@ public abstract class AbstractService<E extends Entity, PK> implements CreateEnt
         }
     }
 
-    protected void tryValidate(E bean) {
+    protected void tryValidate(Object data, String property, Class... groups) {
+        validate(() -> validator.validateProperty(data, property, groups));
+    }
+
+    protected void tryValidate(Class type, String property, Object value, Class... groups) {
+        validate(() -> validator.validateValue(type, property, value, groups));
+    }
+
+    protected void tryValidate(Object data, Class... groups) {
+        validate(() -> validator.validate(data, groups));
+    }
+
+    private <T> void validate(Supplier<Set<ConstraintViolation<T>>> validatorSetFunction) {
         if (validator == null) {
             logger.warn("validator is null!");
             return;
         }
         SimpleValidateResults results = new SimpleValidateResults();
-        validator.validate(bean).forEach(violation -> results.addResult(violation.getPropertyPath().toString(), violation.getMessage()));
+        validatorSetFunction.get()
+                .forEach(violation -> results.addResult(violation.getPropertyPath().toString(), violation.getMessage()));
         if (!results.isSuccess())
             throw new ValidationException(results);
     }

+ 1 - 1
hsweb-commons/hsweb-commons-service/hsweb-commons-service-simple/src/main/java/org/hswebframework/web/service/AbstractTreeSortService.java

@@ -64,7 +64,7 @@ public abstract class AbstractTreeSortService<E extends TreeSortSupportEntity<PK
 
     @Override
     public List<PK> insertBatch(Collection<E> data) {
-        return data.parallelStream()
+        return data.stream()
                 .map(this::insert)
                 .collect(Collectors.toList());
     }

+ 64 - 0
hsweb-commons/hsweb-commons-service/hsweb-commons-service-simple/src/main/java/org/hswebframework/web/service/EnableCacheGernericEntityService.java

@@ -0,0 +1,64 @@
+package org.hswebframework.web.service;
+
+import org.hswebframework.web.commons.entity.GenericEntity;
+import org.springframework.cache.annotation.CacheEvict;
+import org.springframework.cache.annotation.Cacheable;
+
+import java.util.List;
+
+/**
+ * TODO 完成注释
+ *
+ * @author zhouhao
+ */
+public abstract class EnableCacheGernericEntityService<E extends GenericEntity<PK>, PK> extends GenericEntityService<E, PK> {
+
+    @Override
+    @Cacheable(key = "'ids:'+#id.hashCode()", condition = "#id!=null")
+    public List<E> selectByPk(List<PK> id) {
+        return super.selectByPk(id);
+    }
+
+    @Override
+    @Cacheable(key = "'id:'+#pk")
+    public E selectByPk(PK pk) {
+        return super.selectByPk(pk);
+    }
+
+    @Override
+    @CacheEvict(allEntries = true)
+    public int updateByPk(List<E> data) {
+        return super.updateByPk(data);
+    }
+
+    @Override
+    @CacheEvict(allEntries = true)
+    public int updateByPk(PK pk, E entity) {
+        return super.updateByPk(pk, entity);
+    }
+
+    @Override
+    @CacheEvict(allEntries = true)
+    protected int updateByPk(E entity) {
+        return super.updateByPk(entity);
+    }
+
+    @Override
+    @CacheEvict(allEntries = true)
+    public PK insert(E entity) {
+        return super.insert(entity);
+    }
+
+    @Override
+    @CacheEvict(allEntries = true)
+    public int deleteByPk(PK pk) {
+        return super.deleteByPk(pk);
+    }
+
+    @Override
+    @CacheEvict(allEntries = true)
+    public PK saveOrUpdate(E entity) {
+        return super.saveOrUpdate(entity);
+    }
+
+}

+ 7 - 3
hsweb-commons/hsweb-commons-service/hsweb-commons-service-simple/src/main/java/org/hswebframework/web/service/GenericEntityService.java

@@ -22,9 +22,12 @@ import org.hswebframework.web.commons.entity.GenericEntity;
 import org.hswebframework.web.commons.entity.RecordCreationEntity;
 import org.hswebframework.web.dao.CrudDao;
 import org.hswebframework.web.id.IDGenerator;
-import org.hswebframwork.utils.ClassUtils;
+import org.hswebframework.utils.ClassUtils;
+import org.hswebframework.web.validator.group.CreateGroup;
+import org.hswebframework.web.validator.group.UpdateGroup;
 import org.springframework.transaction.annotation.Transactional;
 
+import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -63,7 +66,7 @@ public abstract class GenericEntityService<E extends GenericEntity<PK>, PK>
     @Override
     public int updateByPk(PK pk, E entity) {
         entity.setId(pk);
-        tryValidate(entity);
+        tryValidate(entity, UpdateGroup.class);
         return createUpdate(entity)
                 //如果是RecordCreationEntity则不修改creator_id和creator_time
                 .when(ClassUtils.instanceOf(getEntityType(), RecordCreationEntity.class),
@@ -103,7 +106,7 @@ public abstract class GenericEntityService<E extends GenericEntity<PK>, PK>
             tryValidateProperty(selectByPk(entity.getId()) == null, "id", entity.getId() + "已存在");
         }
         if (entity.getId() == null) entity.setId(getIDGenerator().generate());
-        tryValidate(entity);
+        tryValidate(entity, CreateGroup.class);
         getDao().insert(entity);
         return entity.getId();
     }
@@ -117,6 +120,7 @@ public abstract class GenericEntityService<E extends GenericEntity<PK>, PK>
 
     @Override
     public List<E> selectByPk(List<PK> id) {
+        if (id == null || id.isEmpty()) return new ArrayList<>();
         return createQuery().where().in(GenericEntity.id, id).listNoPaging();
     }
 }

+ 11 - 2
hsweb-commons/hsweb-commons-utils/src/main/java/org/hswebframework/web/AopUtils.java

@@ -29,16 +29,25 @@ import java.util.Map;
 
 public class AopUtils {
 
-    public static <T extends Annotation> T findAnnotation(Class targetClass, Method method, Class<T> annClass) {
+    public static <T extends Annotation> T findMethodAnnotation(Class targetClass, Method method, Class<T> annClass) {
         Method m = method;
         T a = AnnotationUtils.findAnnotation(m, annClass);
         if (a != null) return a;
         m = ClassUtils.getMostSpecificMethod(m, targetClass);
         a = AnnotationUtils.findAnnotation(m, annClass);
-        if (a != null) return a;
+        return a;
+    }
+
+    public static <T extends Annotation> T findAnnotation(Class targetClass, Class<T> annClass) {
         return AnnotationUtils.findAnnotation(targetClass, annClass);
     }
 
+    public static <T extends Annotation> T findAnnotation(Class targetClass, Method method, Class<T> annClass) {
+        T a = findMethodAnnotation(targetClass, method, annClass);
+        if (a != null) return a;
+        return findAnnotation(targetClass, annClass);
+    }
+
     public static <T extends Annotation> T findAnnotation(JoinPoint pjp, Class<T> annClass) {
         MethodSignature signature = (MethodSignature) pjp.getSignature();
         Method m = signature.getMethod();

+ 56 - 8
hsweb-commons/hsweb-commons-utils/src/main/java/org/hswebframework/web/ThreadLocalUtils.java

@@ -23,6 +23,16 @@ import java.util.Map;
 import java.util.function.Supplier;
 
 /**
+ * ThreadLocal 工具类,通过在ThreadLocal存储map信息,来实现在ThreadLocal中维护多个信息
+ * <br>e.g.<code>
+ * ThreadLocalUtils.put("key",value);<br>
+ * ThreadLocalUtils.get("key");<br>
+ * ThreadLocalUtils.remove("key");<br>
+ * ThreadLocalUtils.getAndRemove("key");<br>
+ * ThreadLocalUtils.get("key",()-&gt;defaultValue);<br>
+ * ThreadLocalUtils.clear();<br>
+ * </code>
+ *
  * @author zhouhao
  * @since 2.0
  */
@@ -30,39 +40,77 @@ import java.util.function.Supplier;
 public class ThreadLocalUtils {
     private static final ThreadLocal<Map<String, Object>> local = ThreadLocal.withInitial(HashMap::new);
 
+    /**
+     * 设置一个值到ThreadLocal
+     *
+     * @param key   键
+     * @param value 值
+     * @param <T>   值的类型
+     * @return 被放入的值
+     * @see Map#put(Object, Object)
+     */
     public static <T> T put(String key, T value) {
         local.get().put(key, value);
         return value;
     }
 
+    /**
+     * 删除参数对应的值
+     *
+     * @param key
+     * @see Map#remove(Object)
+     */
     public static void remove(String key) {
         local.get().remove(key);
     }
 
+    /**
+     * 清空ThreadLocal
+     *
+     * @see Map#clear()
+     */
     public static void clear() {
         local.remove();
     }
 
+    /**
+     * 从ThreadLocal中获取值
+     *
+     * @param key 键
+     * @param <T> 值泛型
+     * @return 值, 不存在则返回null, 如果类型与泛型不一致, 可能抛出{@link ClassCastException}
+     * @see Map#get(Object)
+     * @see ClassCastException
+     */
     public static <T> T get(String key) {
         return ((T) local.get().get(key));
     }
 
     /**
+     * 从ThreadLocal中获取值,并指定一个当值不存在的提供者
+     *
+     * @see Supplier
      * @since 3.0
      */
-    public static <T> T get(String key, Supplier<T> other) {
-        T val = ((T) local.get().get(key));
-        if (null != val) return val;
-        val = other.get();
-        local.get().put(key, val);
-        return val;
+    public static <T> T get(String key, Supplier<T> supplierOnNull) {
+        return ((T) local.get().computeIfAbsent(key, k -> supplierOnNull.get()));
     }
 
+    /**
+     * 获取一个值后然后删除掉
+     *
+     * @param key 键
+     * @param <T> 值类型
+     * @return 值, 不存在则返回null
+     * @see this#get(String)
+     * @see this#remove(String)
+     */
     public static <T> T getAndRemove(String key) {
         try {
-            return ((T) local.get().get(key));
+            return get(key);
         } finally {
-            local.get().remove(key);
+            remove(key);
         }
     }
+
 }

+ 9 - 9
hsweb-concurrent/hsweb-concurrent-lock/hsweb-concurrent-lock-api/src/main/java/org/hswebframework/web/concurrent/lock/AbstactLocakManager.java

@@ -10,30 +10,30 @@ import java.util.concurrent.locks.ReadWriteLock;
  *
  * @author zhouhao
  */
-public abstract class AbstactLocakManager implements LockManager {
+public abstract class AbstractLockManager implements LockManager {
     private final Map<String, Lock>          lockStore          = new HashMap<>(128);
     private final Map<String, ReadWriteLock> readWriteLockStore = new HashMap<>(128);
 
     @Override
-    public Lock getLock(String lockKey) {
-        Lock lock = lockStore.get(lockKey);
+    public Lock getLock(String lockName) {
+        Lock lock = lockStore.get(lockName);
         if (lock != null) return lock;
         synchronized (lockStore) {
-            return lockStore.computeIfAbsent(lockKey, this::createLock);
+            return lockStore.computeIfAbsent(lockName, this::createLock);
         }
     }
 
     @Override
-    public ReadWriteLock getReadWriteLock(String lockKey) {
-        ReadWriteLock lock = readWriteLockStore.get(lockKey);
+    public ReadWriteLock getReadWriteLock(String lockName) {
+        ReadWriteLock lock = readWriteLockStore.get(lockName);
         if (lock != null) return lock;
         synchronized (readWriteLockStore) {
-            return readWriteLockStore.computeIfAbsent(lockKey, this::createReadWriteLock);
+            return readWriteLockStore.computeIfAbsent(lockName, this::createReadWriteLock);
         }
     }
 
-    protected abstract Lock createLock(String lockKey);
+    protected abstract Lock createLock(String lockName);
 
-    protected abstract ReadWriteLock createReadWriteLock(String lockKey);
+    protected abstract ReadWriteLock createReadWriteLock(String lockName);
 
 }

+ 16 - 2
hsweb-concurrent/hsweb-concurrent-lock/hsweb-concurrent-lock-api/src/main/java/org/hswebframework/web/concurrent/lock/LockManager.java

@@ -12,7 +12,21 @@ import java.util.concurrent.locks.ReadWriteLock;
  * @since 3.0
  */
 public interface LockManager {
-    Lock getLock(String lockKey);
+    /**
+     * 根据锁名称获取锁,相同的名称,则锁也相同
+     *
+     * @param lockName 锁名称
+     * @return 锁对象
+     * @see Lock
+     */
+    Lock getLock(String lockName);
 
-    ReadWriteLock getReadWriteLock(String lockKey);
+    /**
+     * 根据锁名称获取读写锁,相同的名称,则锁也相同
+     *
+     * @param lockName 锁名称
+     * @return 读写锁对象
+     * @see ReadWriteLock
+     */
+    ReadWriteLock getReadWriteLock(String lockName);
 }

+ 4 - 4
hsweb-concurrent/hsweb-concurrent-lock/hsweb-concurrent-lock-api/src/main/java/org/hswebframework/web/concurrent/lock/SimpleLockManager.java

@@ -11,17 +11,17 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
  * @author zhouhao
  * @see ReentrantLock
  * @see ReentrantReadWriteLock
- * @see AbstactLocakManager
+ * @see AbstractLockManager
  * @since 3.0
  */
-public class SimpleLockManager extends AbstactLocakManager {
+public class SimpleLockManager extends AbstractLockManager {
     @Override
-    protected synchronized Lock createLock(String lockKey) {
+    protected Lock createLock(String lockName) {
         return new ReentrantLock();
     }
 
     @Override
-    protected synchronized ReadWriteLock createReadWriteLock(String lockKey) {
+    protected ReadWriteLock createReadWriteLock(String lockName) {
         return new ReentrantReadWriteLock();
     }
 }

+ 65 - 0
hsweb-concurrent/hsweb-concurrent-lock/hsweb-concurrent-lock-api/src/main/java/org/hswebframework/web/concurrent/lock/annotation/Lock.java

@@ -0,0 +1,65 @@
+package org.hswebframework.web.concurrent.lock.annotation;
+
+import java.lang.annotation.*;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 锁注解,在方法上注解,则对此方法加锁
+ *
+ * @author zhouhao
+ * @see org.hswebframework.web.concurrent.lock.LockManager
+ * @see java.util.concurrent.locks.Lock
+ * @since 3.0
+ */
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+@Documented
+public @interface Lock {
+    /**
+     * 锁名,支持表达式,表达式使用 ${} 进行标识;如果此值为空,则使用方法名称作为锁名
+     * e.g.
+     * <pre>
+     *     &#064;Lock("my_lock_${#id}")
+     *     public void foo(String id){
+     *
+     *     }
+     *
+     *     &#064;Lock(value="my_lock_${#id}",condition="#id!=null")
+     *     public void foo(String id){
+     *
+     *     }
+     * </pre>
+     *
+     * @return 锁名称, 支持spel表达式
+     * @see org.hswebframework.web.concurrent.lock.LockManager#getLock(String)
+     */
+    String[] value() default {};
+
+    /**
+     * 锁的条件表达式,当满足条件的时候才执行锁
+     * e.g.
+     * <pre>
+     *     &#064;Lock(value="my_lock_${#id}",condition="#id!=null")
+     *     public void foo(String id){
+     *
+     *     }
+     * </pre>
+     *
+     * @return 条件表达式
+     */
+    String condition() default "";
+
+
+    /**
+     * 超时时间,超过此时间不能获取锁则抛出异常{@link InterruptedException},如果设置为-1,则认为不设置超时时间
+     *
+     * @return 超时时间, 默认10秒
+     */
+    long timeout() default 10;
+
+    /**
+     * @return 超时时间单位, 秒
+     */
+    TimeUnit timeUnit() default TimeUnit.SECONDS;
+}

+ 64 - 0
hsweb-concurrent/hsweb-concurrent-lock/hsweb-concurrent-lock-api/src/main/java/org/hswebframework/web/concurrent/lock/annotation/ReadLock.java

@@ -0,0 +1,64 @@
+package org.hswebframework.web.concurrent.lock.annotation;
+
+import java.lang.annotation.*;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.ReadWriteLock;
+
+/**
+ * 读锁注解,在方法上注解,则对此方法加锁.
+ *
+ * @author zhouhao
+ * @see org.hswebframework.web.concurrent.lock.LockManager
+ * @see ReadWriteLock#readLock()
+ * @since 3.0
+ */
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+@Documented
+public @interface ReadLock {
+    /**
+     * 锁名,支持表达式,表达式使用 ${} 进行标识;如果此值为空,则使用方法名称作为锁名
+     * e.g.
+     * <pre>
+     *     &#064;ReadLock("my_lock_${#id}")
+     *     public void foo(String id){
+     *
+     *     }
+     *
+     *     &#064;ReadLock(value="my_lock_${#id}",condition="#id!=null")
+     *     public void foo(String id){
+     *
+     *     }
+     * </pre>
+     *
+     * @return 锁名称, 支持spel表达式
+     * @see org.hswebframework.web.concurrent.lock.LockManager#getReadWriteLock(String)
+     */
+    String[] value() default {};
+
+    /**
+     * 锁的条件表达式,当满足条件的时候才执行锁
+     * e.g.
+     * <pre>
+     *     &#064;ReadLock(value="my_lock_${#id}",condition="#id!=null")
+     *     public void foo(String id){
+     *
+     *     }
+     * </pre>
+     * @return 条件表达式
+     */
+    String condition() default "";
+
+    /**
+     * 超时时间,超过此时间不能获取锁则抛出异常{@link InterruptedException},如果设置为-1,则认为不设置超时时间
+     *
+     * @return 超时时间, 默认10秒
+     */
+    long timeout() default 10;
+
+    /**
+     * @return 超时时间单位, 秒
+     */
+    TimeUnit timeUnit() default TimeUnit.SECONDS;
+}

+ 75 - 0
hsweb-concurrent/hsweb-concurrent-lock/hsweb-concurrent-lock-api/src/main/java/org/hswebframework/web/concurrent/lock/annotation/WriteLock.java

@@ -0,0 +1,75 @@
+package org.hswebframework.web.concurrent.lock.annotation;
+
+import java.lang.annotation.*;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.ReadWriteLock;
+
+/**
+ * 写锁注解,在方法上注解,则对此方法加锁.<br>
+ * e.g.
+ * <pre>
+ *     &#064;ReadLock("my_lock_${#id}")
+ *     public void foo(String id){
+ *          //do some thing
+ *     }
+ * </pre>
+ * 满足条件才锁
+ * e.g.
+ * <pre>
+ *     &#064;WriteLock(value="my_lock_${#id}",condition="#id!=null")
+ *     public void foo(String id){
+ *
+ *     }
+ * </pre>
+ *
+ * @author zhouhao
+ * @see org.hswebframework.web.concurrent.lock.LockManager
+ * @see ReadWriteLock#writeLock()
+ * @since 3.0
+ */
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+@Documented
+public @interface WriteLock {
+    /**
+     * 锁名,支持表达式,表达式使用 ${} 进行标识;如果此值为空,则使用方法名称作为锁名
+     * e.g.
+     * <pre>
+     *     &#064;ReadLock("my_lock_${#id}")
+     *     public void foo(String id){
+     *
+     *     }
+     * </pre>
+     *
+     * @return 锁名称, 支持spel表达式
+     * @see org.hswebframework.web.concurrent.lock.LockManager#getReadWriteLock(String)
+     */
+    String[] value() default {};
+
+    /**
+     * 锁的条件表达式,当满足条件的时候才执行锁
+     * e.g.
+     * <pre>
+     *     &#064;WriteLock(value="my_lock_${#id}",condition="#id!=null")
+     *     public void foo(String id){
+     *
+     *     }
+     * </pre>
+     *
+     * @return 条件表达式
+     */
+    String condition() default "";
+
+    /**
+     * 超时时间,超过此时间不能获取锁则抛出异常{@link InterruptedException},如果设置为-1,则认为不设置超时时间
+     *
+     * @return 超时时间, 默认10秒
+     */
+    long timeout() default 10;
+
+    /**
+     * @return 超时时间单位, 秒
+     */
+    TimeUnit timeUnit() default TimeUnit.SECONDS;
+}

+ 9 - 10
hsweb-concurrent/hsweb-concurrent-lock/hsweb-concurrent-lock-redis/src/main/java/org/hswebframework/web/concurrent/lock/redis/RedissonLockManager.java

@@ -1,30 +1,29 @@
 package org.hswebframework.web.concurrent.lock.redis;
 
-import org.hswebframework.web.concurrent.lock.AbstactLocakManager;
-import org.redisson.Redisson;
+import org.hswebframework.web.concurrent.lock.AbstractLockManager;
+import org.redisson.api.RedissonClient;
 
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReadWriteLock;
 
 /**
- *
  * @author zhouhao
  */
-public class RedissonLockManager extends AbstactLocakManager {
-    private Redisson redisson;
+public class RedissonLockManager extends AbstractLockManager {
+    private RedissonClient redisson;
 
-    public RedissonLockManager(Redisson redisson) {
+    public RedissonLockManager(RedissonClient redisson) {
         if (null == redisson) throw new NullPointerException();
         this.redisson = redisson;
     }
 
     @Override
-    protected Lock createLock(String lockKey) {
-        return redisson.getLock(lockKey);
+    protected Lock createLock(String lockName) {
+        return redisson.getLock(lockName);
     }
 
     @Override
-    protected ReadWriteLock createReadWriteLock(String lockKey) {
-        return redisson.getReadWriteLock(lockKey);
+    protected ReadWriteLock createReadWriteLock(String lockName) {
+        return redisson.getReadWriteLock(lockName);
     }
 }

+ 16 - 0
hsweb-concurrent/hsweb-concurrent-lock/hsweb-concurrent-lock-starter/pom.xml

@@ -28,5 +28,21 @@
             <version>${project.version}</version>
             <optional>true</optional>
         </dependency>
+        <dependency>
+            <groupId>org.hswebframework.web</groupId>
+            <artifactId>hsweb-boost-aop</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.hswebframework.web</groupId>
+            <artifactId>hsweb-tests</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.h2database</groupId>
+            <artifactId>h2</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 </project>

+ 99 - 0
hsweb-concurrent/hsweb-concurrent-lock/hsweb-concurrent-lock-starter/src/main/java/org/hswebframework/web/concurrent/lock/starter/AopLockAdvisor.java

@@ -0,0 +1,99 @@
+package org.hswebframework.web.concurrent.lock.starter;
+
+import org.aopalliance.intercept.MethodInterceptor;
+import org.aopalliance.intercept.MethodInvocation;
+import org.hswebframework.web.AopUtils;
+import org.hswebframework.web.boost.aop.context.MethodInterceptorHolder;
+import org.hswebframework.web.concurrent.lock.LockManager;
+import org.hswebframework.web.concurrent.lock.annotation.Lock;
+import org.hswebframework.web.concurrent.lock.annotation.ReadLock;
+import org.hswebframework.web.concurrent.lock.annotation.WriteLock;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * TODO 完成注释
+ *
+ * @author zhouhao
+ */
+public class AopLockAdvisor extends StaticMethodMatcherPointcutAdvisor {
+
+    public AopLockAdvisor(LockManager lockManager) {
+        Objects.requireNonNull(lockManager);
+        setAdvice((MethodInterceptor) methodInvocation -> {
+            MethodInterceptorHolder holder = MethodInterceptorHolder.create(methodInvocation);
+            Lock lockAnn = holder.findMethodAnnotation(Lock.class);
+            ReadLock readLockAnn = holder.findMethodAnnotation(ReadLock.class);
+            WriteLock writeLock = holder.findMethodAnnotation(WriteLock.class);
+            List<LockProcessor> lockProcessors = new ArrayList<>();
+            if (null != lockAnn) {
+                lockProcessors.add(initLockInfo(lockAnn.timeout(), lockAnn.timeUnit(),
+                        LockProcessor.<Lock, java.util.concurrent.locks.Lock>build(lockAnn, holder)
+                                .lockNameIs(Lock::value)
+                                .lockIs(lockManager::getLock)));
+            }
+            if (null != readLockAnn) {
+                lockProcessors.add(initLockInfo(readLockAnn.timeout(), readLockAnn.timeUnit(),
+                        LockProcessor.<ReadLock, java.util.concurrent.locks.Lock>build(readLockAnn, holder)
+                                .lockNameIs(ReadLock::value)
+                                .lockIs(name -> lockManager.getReadWriteLock(name).readLock())));
+            }
+            if (null != writeLock) {
+                lockProcessors.add(initLockInfo(writeLock.timeout(), writeLock.timeUnit(),
+                        LockProcessor.<WriteLock, java.util.concurrent.locks.Lock>build(writeLock, holder)
+                                .lockNameIs(WriteLock::value)
+                                .lockIs(name -> lockManager.getReadWriteLock(name).writeLock())));
+            }
+
+            try {
+                for (LockProcessor processor : lockProcessors) {
+                    Throwable e = processor.doLock();
+                    if (e != null) {
+                        throw e;
+                    }
+                }
+                return methodInvocation.proceed();
+            } finally {
+                for (LockProcessor processor : lockProcessors) {
+                    processor.doUnlock();
+                }
+            }
+        });
+    }
+
+    protected <A extends Annotation> LockProcessor<A, java.util.concurrent.locks.Lock> initLockInfo(long timeout, TimeUnit timeUnit, LockProcessor<A, java.util.concurrent.locks.Lock> lockProcessor) {
+        return lockProcessor.lock(lock -> {
+            try {
+                lock.tryLock(timeout, timeUnit);
+                return null;
+            } catch (InterruptedException e) {
+                return e;
+            }
+        }).unlock(lock -> {
+            try {
+                lock.unlock();
+                return null;
+            } catch (Throwable e) {
+                return e;
+            }
+        }).init();
+    }
+
+
+    @Override
+    public boolean matches(Method method, Class<?> aClass) {
+        Lock lock = AopUtils.findMethodAnnotation(aClass, method, Lock.class);
+        if (null != lock) return true;
+        ReadLock readLock = AopUtils.findMethodAnnotation(aClass, method, ReadLock.class);
+        if (null != readLock) return true;
+        WriteLock writeLock = AopUtils.findMethodAnnotation(aClass, method, WriteLock.class);
+        if (null != writeLock) return true;
+        return false;
+    }
+}

+ 8 - 3
hsweb-concurrent/hsweb-concurrent-lock/hsweb-concurrent-lock-starter/src/main/java/org/hswebframework/web/concurrent/lock/starter/LockFactoryAutoConfiguration.java

@@ -2,6 +2,7 @@ package org.hswebframework.web.concurrent.lock.starter;
 
 import org.hswebframework.web.concurrent.lock.LockManager;
 import org.hswebframework.web.concurrent.lock.SimpleLockManager;
+import org.springframework.boot.autoconfigure.AutoConfigureAfter;
 import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 import org.springframework.context.annotation.Bean;
@@ -13,12 +14,16 @@ import org.springframework.context.annotation.Configuration;
  * @author zhouhao
  */
 @Configuration
-@ImportAutoConfiguration(RedisLockFactoryAutoConfiguration.class)
-public class LockFactoryAutoConfiguration {
+public class LockManagerAutoConfiguration {
 
     @Bean
     @ConditionalOnMissingBean(LockManager.class)
-    public SimpleLockManager simpleLockFactory() {
+    public LockManager lockManager() {
         return new SimpleLockManager();
     }
+
+    @Bean
+    public AopLockAdvisor aopLockAdvisor(LockManager lockManager) {
+        return new AopLockAdvisor(lockManager);
+    }
 }

+ 120 - 0
hsweb-concurrent/hsweb-concurrent-lock/hsweb-concurrent-lock-starter/src/main/java/org/hswebframework/web/concurrent/lock/starter/LockProcessor.java

@@ -0,0 +1,120 @@
+package org.hswebframework.web.concurrent.lock.starter;
+
+
+import org.hswebframework.web.AopUtils;
+import org.hswebframework.web.ExpressionUtils;
+import org.hswebframework.web.boost.aop.context.MethodInterceptorHolder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.util.StringUtils;
+
+import java.lang.annotation.Annotation;
+import java.util.*;
+import java.util.function.Function;
+
+/**
+ * TODO 完成注释
+ *
+ * @author zhouhao
+ */
+@SuppressWarnings("unchecked")
+public class LockProcessor<A extends Annotation, L> {
+
+    private Logger logger = LoggerFactory.getLogger(this.getClass());
+
+    private A lockAnn;
+
+    private MethodInterceptorHolder interceptorHolder;
+
+    private Function<A, String[]> lockNameGetter;
+
+    private Function<String, L> lockGetter;
+
+    private Function<L, Throwable> lockAccepter;
+
+    private Function<L, Throwable> unlockAccepter;
+
+    private Map<String, L> lockStore = new HashMap<>();
+
+    private LockProcessor() {
+    }
+
+    public static <A extends Annotation, L> LockProcessor<A, L> build(A annotation, MethodInterceptorHolder holder) {
+        LockProcessor<A, L> alLockProcessor = new LockProcessor<>();
+        alLockProcessor.lockAnn = annotation;
+        alLockProcessor.interceptorHolder = holder;
+        return alLockProcessor;
+    }
+
+    public LockProcessor<A, L> lockNameIs(Function<A, String[]> lockNameGetter) {
+        this.lockNameGetter = lockNameGetter;
+        return this;
+    }
+
+    public LockProcessor<A, L> lockIs(Function<String, L> lockGetter) {
+        this.lockGetter = lockGetter;
+        return this;
+    }
+
+    public LockProcessor<A, L> lock(Function<L, Throwable> lockAccepter) {
+        this.lockAccepter = lockAccepter;
+        return this;
+    }
+
+    public LockProcessor<A, L> unlock(Function<L, Throwable> unlockAccepter) {
+        this.unlockAccepter = unlockAccepter;
+        return this;
+    }
+
+    public LockProcessor<A, L> init() {
+        Objects.requireNonNull(lockAnn);
+        Objects.requireNonNull(interceptorHolder);
+        Objects.requireNonNull(lockNameGetter);
+        String[] lockNameArr = lockNameGetter.apply(lockAnn);
+        if (lockNameArr.length == 0) {
+            String name = createLockName(null);
+            lockStore.put(name, lockGetter.apply(name));
+        } else {
+            for (String expression : lockNameArr) {
+                String name = createLockName(expression);
+                lockStore.put(name, lockGetter.apply(name));
+            }
+        }
+        return this;
+    }
+
+    protected String createLockName(String expression) {
+        try {
+            if (StringUtils.isEmpty(expression)) {
+                return interceptorHolder.getMethod().getName().concat("_").concat(interceptorHolder.getId());
+            }
+            return ExpressionUtils.analytical(expression, interceptorHolder.getArgs(), "spel");
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private List<L> successLock = new ArrayList<>();
+
+    public Throwable doLock() {
+        Throwable lockError = null;
+        for (Map.Entry<String, L> lock : lockStore.entrySet()) {
+            Throwable error = lockAccepter.apply(lock.getValue());
+            if (error == null) {
+                successLock.add(lock.getValue());
+            } else {
+                lockError = error;
+            }
+        }
+        return lockError;
+    }
+
+    public void doUnlock() {
+        for (L lock : successLock) {
+            Throwable error = unlockAccepter.apply(lock);
+            if (null != error)
+                logger.error("unlock {} error", interceptorHolder.getMethod(), error);
+        }
+    }
+
+}

+ 0 - 0
hsweb-concurrent/hsweb-concurrent-lock/hsweb-concurrent-lock-starter/src/main/java/org/hswebframework/web/concurrent/lock/starter/RedisLockFactoryAutoConfiguration.java


Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików