فهرست منبع

初始化代码

liuyc 3 سال پیش
والد
کامیت
796096aa79
100فایلهای تغییر یافته به همراه7714 افزوده شده و 2 حذف شده
  1. 42 2
      README.md
  2. 21 0
      blade-auth/Dockerfile
  3. 32 0
      blade-auth/README.md
  4. 295 0
      blade-auth/blade-auth.iml
  5. 140 0
      blade-auth/pom.xml
  6. 38 0
      blade-auth/src/main/java/org/springblade/auth/AuthApplication.java
  7. 117 0
      blade-auth/src/main/java/org/springblade/auth/config/BladeAuthorizationServerConfiguration.java
  8. 59 0
      blade-auth/src/main/java/org/springblade/auth/config/BladeResourceServerConfiguration.java
  9. 66 0
      blade-auth/src/main/java/org/springblade/auth/config/JwtTokenStoreConfiguration.java
  10. 56 0
      blade-auth/src/main/java/org/springblade/auth/config/SecurityConfiguration.java
  11. 53 0
      blade-auth/src/main/java/org/springblade/auth/constant/AuthConstant.java
  12. 88 0
      blade-auth/src/main/java/org/springblade/auth/endpoint/BladeSocialEndpoint.java
  13. 100 0
      blade-auth/src/main/java/org/springblade/auth/endpoint/BladeTokenEndPoint.java
  14. 52 0
      blade-auth/src/main/java/org/springblade/auth/granter/BladeTokenGranter.java
  15. 82 0
      blade-auth/src/main/java/org/springblade/auth/granter/CaptchaTokenGranter.java
  16. 130 0
      blade-auth/src/main/java/org/springblade/auth/granter/SocialTokenGranter.java
  17. 51 0
      blade-auth/src/main/java/org/springblade/auth/service/BladeClientDetailsServiceImpl.java
  18. 99 0
      blade-auth/src/main/java/org/springblade/auth/service/BladeUserDetails.java
  19. 212 0
      blade-auth/src/main/java/org/springblade/auth/service/BladeUserDetailsServiceImpl.java
  20. 86 0
      blade-auth/src/main/java/org/springblade/auth/support/BladeJwtTokenEnhancer.java
  21. 50 0
      blade-auth/src/main/java/org/springblade/auth/support/BladeNoOpPasswordEncoder.java
  22. 39 0
      blade-auth/src/main/java/org/springblade/auth/support/BladePasswordEncoder.java
  23. 66 0
      blade-auth/src/main/java/org/springblade/auth/support/BladePasswordEncoderFactories.java
  24. 182 0
      blade-auth/src/main/java/org/springblade/auth/utils/TokenUtil.java
  25. 15 0
      blade-auth/src/main/resources/application-dev.yml
  26. 15 0
      blade-auth/src/main/resources/application-prod.yml
  27. 15 0
      blade-auth/src/main/resources/application-test.yml
  28. 50 0
      blade-auth/src/main/resources/application.yml
  29. 127 0
      blade-common/blade-common.iml
  30. 46 0
      blade-common/pom.xml
  31. 59 0
      blade-common/src/main/java/org/springblade/common/cache/CacheNames.java
  32. 32 0
      blade-common/src/main/java/org/springblade/common/config/BladeCommonConfiguration.java
  33. 77 0
      blade-common/src/main/java/org/springblade/common/constant/CommonConstant.java
  34. 230 0
      blade-common/src/main/java/org/springblade/common/constant/LauncherConstant.java
  35. 66 0
      blade-common/src/main/java/org/springblade/common/constant/TenantConstant.java
  36. 61 0
      blade-common/src/main/java/org/springblade/common/launch/LauncherServiceImpl.java
  37. 26 0
      blade-common/src/main/java/org/springblade/common/utils/CommonUtil.java
  38. 8 0
      blade-common/src/main/resources/banner.txt
  39. 15 0
      blade-gateway/Dockerfile
  40. 171 0
      blade-gateway/blade-gateway.iml
  41. 130 0
      blade-gateway/pom.xml
  42. 39 0
      blade-gateway/src/main/java/org/springblade/gateway/GateWayApplication.java
  43. 45 0
      blade-gateway/src/main/java/org/springblade/gateway/config/ErrorHandlerConfiguration.java
  44. 81 0
      blade-gateway/src/main/java/org/springblade/gateway/config/RouterFunctionConfiguration.java
  45. 101 0
      blade-gateway/src/main/java/org/springblade/gateway/dynamic/DynamicRouteService.java
  46. 94 0
      blade-gateway/src/main/java/org/springblade/gateway/dynamic/DynamicRouteServiceListener.java
  47. 41 0
      blade-gateway/src/main/java/org/springblade/gateway/dynamic/GatewayFilter.java
  48. 41 0
      blade-gateway/src/main/java/org/springblade/gateway/dynamic/GatewayPredicate.java
  49. 57 0
      blade-gateway/src/main/java/org/springblade/gateway/dynamic/GatewayRoute.java
  50. 117 0
      blade-gateway/src/main/java/org/springblade/gateway/filter/AuthFilter.java
  51. 112 0
      blade-gateway/src/main/java/org/springblade/gateway/filter/GlobalRequestLogFilter.java
  52. 99 0
      blade-gateway/src/main/java/org/springblade/gateway/filter/GlobalResponseLogFilter.java
  53. 63 0
      blade-gateway/src/main/java/org/springblade/gateway/filter/RequestFilter.java
  54. 96 0
      blade-gateway/src/main/java/org/springblade/gateway/handler/ErrorExceptionHandler.java
  55. 41 0
      blade-gateway/src/main/java/org/springblade/gateway/props/AuthProperties.java
  56. 67 0
      blade-gateway/src/main/java/org/springblade/gateway/provider/AuthProvider.java
  57. 49 0
      blade-gateway/src/main/java/org/springblade/gateway/provider/RequestProvider.java
  58. 84 0
      blade-gateway/src/main/java/org/springblade/gateway/provider/ResponseProvider.java
  59. 10 0
      blade-gateway/src/main/resources/application-dev.yml
  60. 15 0
      blade-gateway/src/main/resources/bootstrap.yml
  61. 162 0
      blade-ops-api/blade-flow-api/blade-flow-api.iml
  62. 19 0
      blade-ops-api/blade-flow-api/pom.xml
  63. 61 0
      blade-ops-api/blade-flow-api/src/main/java/org/springblade/flow/core/constant/ProcessConstant.java
  64. 179 0
      blade-ops-api/blade-flow-api/src/main/java/org/springblade/flow/core/entity/BladeFlow.java
  65. 43 0
      blade-ops-api/blade-flow-api/src/main/java/org/springblade/flow/core/entity/FlowEntity.java
  66. 45 0
      blade-ops-api/blade-flow-api/src/main/java/org/springblade/flow/core/enums/FlowModeEnum.java
  67. 100 0
      blade-ops-api/blade-flow-api/src/main/java/org/springblade/flow/core/feign/IFlowClient.java
  68. 58 0
      blade-ops-api/blade-flow-api/src/main/java/org/springblade/flow/core/feign/IFlowClientFallback.java
  69. 66 0
      blade-ops-api/blade-flow-api/src/main/java/org/springblade/flow/core/utils/FlowUtil.java
  70. 71 0
      blade-ops-api/blade-flow-api/src/main/java/org/springblade/flow/core/utils/TaskUtil.java
  71. 151 0
      blade-ops-api/blade-ops-api.iml
  72. 179 0
      blade-ops-api/blade-resource-api/blade-resource-api.iml
  73. 28 0
      blade-ops-api/blade-resource-api/pom.xml
  74. 71 0
      blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/entity/Attach.java
  75. 87 0
      blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/entity/Oss.java
  76. 82 0
      blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/entity/Sms.java
  77. 62 0
      blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/enums/SmsCodeEnum.java
  78. 74 0
      blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/feign/ISmsClient.java
  79. 44 0
      blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/feign/ISmsClientFallback.java
  80. 113 0
      blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/utils/SmsUtil.java
  81. 35 0
      blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/vo/AttachVO.java
  82. 29 0
      blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/vo/OssVO.java
  83. 45 0
      blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/vo/SmsVO.java
  84. 52 0
      blade-ops-api/pom.xml
  85. 15 0
      blade-ops/blade-admin/Dockerfile
  86. 21 0
      blade-ops/blade-admin/README.md
  87. 172 0
      blade-ops/blade-admin/blade-admin.iml
  88. 125 0
      blade-ops/blade-admin/pom.xml
  89. 37 0
      blade-ops/blade-admin/src/main/java/org/springblade/admin/AdminApplication.java
  90. 32 0
      blade-ops/blade-admin/src/main/java/org/springblade/admin/config/AdminConfiguration.java
  91. 52 0
      blade-ops/blade-admin/src/main/java/org/springblade/admin/config/DingTalkConfiguration.java
  92. 71 0
      blade-ops/blade-admin/src/main/java/org/springblade/admin/config/SecurityConfiguration.java
  93. 105 0
      blade-ops/blade-admin/src/main/java/org/springblade/admin/dingtalk/DingTalkNotifier.java
  94. 110 0
      blade-ops/blade-admin/src/main/java/org/springblade/admin/dingtalk/DingTalkService.java
  95. 67 0
      blade-ops/blade-admin/src/main/java/org/springblade/admin/dingtalk/MonitorProperties.java
  96. 75 0
      blade-ops/blade-admin/src/main/java/org/springblade/admin/security/InternalAuthorizationManager.java
  97. 45 0
      blade-ops/blade-admin/src/main/resources/bootstrap.yml
  98. 289 0
      blade-ops/blade-develop/blade-develop.iml
  99. 53 0
      blade-ops/blade-develop/pom.xml
  100. 38 0
      blade-ops/blade-develop/src/main/java/org/springblade/develop/DevelopApplication.java

+ 42 - 2
README.md

@@ -1,3 +1,43 @@
-# bladex
+## 版权声明
+* BladeX是一个商业化软件,系列产品知识产权归**上海布雷德科技有限公司**独立所有
+* 您一旦开始复制、下载、安装或者使用本产品,即被视为完全理解并接受本协议的各项条款
+* 更多详情请看:[BladeX商业授权许可协议](/LICENSE)
 
-bladex
+## 答疑流程
+>1. 遇到问题或Bug
+>2. 业务型问题打断点调试尝试找出问题所在
+>3. 系统型问题通过百度、谷歌、社区查找解决方案
+>4. 未解决问题则进入技术社区进行发帖提问:[https://sns.bladex.vip/](https://sns.bladex.vip/)
+>5. 将帖子地址发至商业群,特别简单三言两语就能描述清楚的也可在答疑时间内发至商业群提问
+>6. 发帖的时候一定要描述清楚,详细描述遇到问题的**重现步骤**、**报错详细信息**、**相关代码与逻辑**、**使用软件版本**以及**操作系统版本**,否则随意发帖提问将会提高我们的答疑难度。
+
+## 答疑时间
+* 工作日:9:00 ~ 17:00 提供答疑,周末、节假日休息,暂停答疑
+* 请勿**私聊提问**,以免被其他用户的消息覆盖从而无法获得答疑
+* 答疑时间外遇到问题可以将问题发帖至[技术社区](https://sns.bladex.vip/),我们后续会逐个回复
+
+## 授权范围
+* 专业版:只可用于**个人学习**及**个人私活**项目,不可用于公司或团队,不可泄露给任何第三方
+* 企业版:可用于**企业名下**的任何项目,企业版员工在**未购买**专业版授权前,只授权开发**所在授权企业名下**的项目,**不得将BladeX用于个人私活**
+* 共同遵守:若甲方需要您提供项目源码,则需代为甲方购买BladeX企业授权,甲方购买后续的所有项目都无需再次购买授权
+
+## 商用权益
+* ✔️ 遵守[商业协议](/LICENSE)的前提下,将BladeX系列产品用于授权范围内的商用项目,并上线运营
+* ✔️ 遵守[商业协议](/LICENSE)的前提下,不限制项目数,不限制服务器数
+* ✔️ 遵守[商业协议](/LICENSE)的前提下,将自行编写的业务代码申请软件著作权
+
+## 何为侵权
+* ❌ 不遵守商业协议,私自销售商业源码
+* ❌ 以任何理由将BladeX源码用于申请软件著作权
+* ❌ 将商业源码以任何途径任何理由泄露给未授权的单位或个人
+* ❌ 开发完毕项目,没有为甲方购买企业授权,向甲方提供了BladeX代码
+* ❌ 基于BladeX拓展研发与BladeX有竞争关系的衍生框架,并将其开源或销售
+
+## 侵权后果
+* 情节较轻:第一次发现警告处理
+* 情节较重:封禁账号,踢出商业群,并保留追究法律责任的权利
+* 情节严重:与本地律师事务所合作,以公司名义起诉侵犯计算机软件著作权
+
+## 举报有奖
+* 向官方提供有用线索并成功捣毁盗版个人或窝点,将会看成果给予 500~10000 不等的现金奖励
+* 官方唯一指定QQ:1272154962

+ 21 - 0
blade-auth/Dockerfile

@@ -0,0 +1,21 @@
+#Docker镜像可以选择以下三种,前两种已经内置字体,第三种为原生的openjdk8-openj9镜像
+#1.FROM bladex/alpine-java:8_server-jre_cn_unlimited
+#2.FROM bladex/alpine-java:openjdk8-openj9_cn_slim
+#3.FROM adoptopenjdk/openjdk8-openj9:jdk8u262-b10_openj9-0.21.0-alpine-slim
+#第3点的tag可以到该地址查询:https://hub.docker.com/r/adoptopenjdk/openjdk8-openj9/tags?page=1&ordering=last_updated&name=alpine-slim
+#版本介绍一览:https://hub.docker.com/r/adoptopenjdk/openjdk8-openj9
+FROM bladex/alpine-java:openjdk8-openj9_cn_slim
+
+MAINTAINER bladejava@qq.com
+
+RUN mkdir -p /blade/auth
+
+WORKDIR /blade/auth
+
+EXPOSE 8100
+
+ADD ./target/blade-auth.jar ./app.jar
+
+ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "app.jar"]
+
+CMD ["--spring.profiles.active=test"]

+ 32 - 0
blade-auth/README.md

@@ -0,0 +1,32 @@
+## 目前主要支持的oauth协议
+一、 授权码模式
+授权码模式(authorization_code)主要针对第三方应用,是最为复杂也最为安全的一种模式,操作步骤如下
+1. 访问地址:http://localhost:8100/oauth/authorize?client_id=blade&redirect_uri=http://example.com&code=233333&response_type=code
+2. 获取跳转后的code值(http://example.com/?code=VhYNLR)之后,调用 http://localhost/blade-auth/oauth/token 传入对应的参数
+
+请求头:
+Authorization : Basic YmxhZGU6YmxhZGU= ("YmxhZGU6YmxhZGU="为clientId:clientSecret串转换为的base64编码)
+
+表单:
+grant_type:authorization_code
+scope:all
+code:VhYNLR
+redirect_uri: http://example.com
+
+二、 密码模式
+密码模式(password)主要针对自家应用,可信度较高,所以可以使用简便安全共存的模式,操作步骤如下
+1. 直接调用 http://localhost/blade-auth/oauth/token 传入对应的参数
+
+请求头:
+Authorization : Basic YmxhZGU6YmxhZGU= ("YmxhZGU6YmxhZGU="为clientId:clientSecret串转换为的base64编码)
+
+表单:
+grant_type:password
+scope:all
+username:admin
+password:123456
+
+## 获取到token后如何获取用户信息
+1. 拼接请求头
+Authorization :bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0ZXN0IjoidGVzdCIsInVzZXJfbmFtZSI6ImFkbWluIiwic2NvcGUiOlsiYWxsIl0sImV4cCI6MTU1MzE2MTA5NSwiYXV0aG9yaXRpZXMiOlsiUk9MRV9VU0VSIl0sImp0aSI6IjE0YmMyYjAyLTgxY2UtNDFiNC04ZTI3LTA5YWE0ZmU4ZWMwYyIsImNsaWVudF9pZCI6ImJsYWRlIn0.jTmioQDq-fSNNn7YCwl3wP0JE-etSWtzLDe545mDbP4
+2. 调用 http://localhost/blade-auth/oauth/user-info 既可获得对应用户信息

+ 295 - 0
blade-auth/blade-auth.iml

@@ -0,0 +1,295 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
+  <component name="FacetManager">
+    <facet type="Spring" name="Spring">
+      <configuration />
+    </facet>
+    <facet type="web" name="Web">
+      <configuration>
+        <webroots />
+      </configuration>
+    </facet>
+  </component>
+  <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
+    <output url="file://$MODULE_DIR$/target/classes" />
+    <output-test url="file://$MODULE_DIR$/target/test-classes" />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
+      <excludeFolder url="file://$MODULE_DIR$/target" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="module" module-name="blade-common" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-core-launch:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-web:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-json:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.module:jackson-module-parameter-names:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-undertow:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: io.undertow:undertow-core:2.1.7.Final" level="project" />
+    <orderEntry type="library" name="Maven: org.jboss.logging:jboss-logging:3.4.2.Final" level="project" />
+    <orderEntry type="library" name="Maven: org.jboss.xnio:xnio-api:3.8.0.Final" level="project" />
+    <orderEntry type="library" name="Maven: org.wildfly.common:wildfly-common:1.5.2.Final" level="project" />
+    <orderEntry type="library" name="Maven: org.wildfly.client:wildfly-client-config:1.0.1.Final" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: org.jboss.xnio:xnio-nio:3.8.0.Final" level="project" />
+    <orderEntry type="library" name="Maven: org.jboss.threads:jboss-threads:3.1.0.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.undertow:undertow-servlet:2.1.7.Final" level="project" />
+    <orderEntry type="library" name="Maven: org.jboss.spec.javax.annotation:jboss-annotations-api_1.3_spec:2.0.1.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.undertow:undertow-websockets-jsr:2.1.7.Final" level="project" />
+    <orderEntry type="library" name="Maven: org.jboss.spec.javax.websocket:jboss-websocket-api_1.1_spec:2.0.0.Final" level="project" />
+    <orderEntry type="library" name="Maven: jakarta.servlet:jakarta.servlet-api:4.0.4" level="project" />
+    <orderEntry type="library" name="Maven: org.glassfish:jakarta.el:3.0.3" level="project" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-starter-ribbon:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-starter-netflix-ribbon:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.netflix.ribbon:ribbon:2.3.0" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: com.netflix.ribbon:ribbon-transport:2.3.0" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: io.reactivex:rxnetty-contexts:0.4.9" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: io.reactivex:rxnetty-servo:0.4.9" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: javax.inject:javax.inject:1" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: io.reactivex:rxnetty:0.4.9" level="project" />
+    <orderEntry type="library" name="Maven: com.netflix.ribbon:ribbon-core:2.3.0" level="project" />
+    <orderEntry type="library" name="Maven: commons-lang:commons-lang:2.6" level="project" />
+    <orderEntry type="library" name="Maven: com.netflix.ribbon:ribbon-httpclient:2.3.0" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: commons-collections:commons-collections:3.2.2" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: com.sun.jersey:jersey-client:1.19.1" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: com.sun.jersey:jersey-core:1.19.1" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: javax.ws.rs:jsr311-api:1.1.1" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: com.sun.jersey.contribs:jersey-apache-client4:1.19.1" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: com.netflix.servo:servo-core:0.12.21" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: com.netflix.netflix-commons:netflix-commons-util:0.3.0" level="project" />
+    <orderEntry type="library" name="Maven: com.netflix.ribbon:ribbon-loadbalancer:2.3.0" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: com.netflix.netflix-commons:netflix-statistics:0.1.1" level="project" />
+    <orderEntry type="library" name="Maven: io.reactivex:rxjava:1.3.8" level="project" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-core-db:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-core-tool:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: com.google.guava:guava:30.1.1-jre" level="project" />
+    <orderEntry type="library" name="Maven: com.google.guava:failureaccess:1.0.1" level="project" />
+    <orderEntry type="library" name="Maven: com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava" level="project" />
+    <orderEntry type="library" name="Maven: com.google.code.findbugs:jsr305:3.0.2" level="project" />
+    <orderEntry type="library" name="Maven: com.google.errorprone:error_prone_annotations:2.5.1" level="project" />
+    <orderEntry type="library" name="Maven: com.google.j2objc:j2objc-annotations:1.3" level="project" />
+    <orderEntry type="library" name="Maven: io.protostuff:protostuff-core:1.6.0" level="project" />
+    <orderEntry type="library" name="Maven: io.protostuff:protostuff-api:1.6.0" level="project" />
+    <orderEntry type="library" name="Maven: io.protostuff:protostuff-runtime:1.6.0" level="project" />
+    <orderEntry type="library" name="Maven: io.protostuff:protostuff-collectionschema:1.6.0" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.module:jackson-module-jaxb-annotations:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: jakarta.activation:jakarta.activation-api:1.2.2" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-validation:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.hibernate.validator:hibernate-validator:6.1.7.Final" level="project" />
+    <orderEntry type="library" name="Maven: jakarta.validation:jakarta.validation-api:2.0.2" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-jdbc:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.zaxxer:HikariCP:3.4.5" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-jdbc:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.baomidou:mybatis-plus-boot-starter:3.5.1" level="project" />
+    <orderEntry type="library" name="Maven: com.baomidou:mybatis-plus:3.5.1" level="project" />
+    <orderEntry type="library" name="Maven: com.baomidou:mybatis-plus-extension:3.5.1" level="project" />
+    <orderEntry type="library" name="Maven: com.baomidou:mybatis-plus-core:3.5.1" level="project" />
+    <orderEntry type="library" name="Maven: com.baomidou:mybatis-plus-annotation:3.5.1" level="project" />
+    <orderEntry type="library" name="Maven: com.github.jsqlparser:jsqlparser:4.3" level="project" />
+    <orderEntry type="library" name="Maven: org.mybatis:mybatis:3.5.9" level="project" />
+    <orderEntry type="library" name="Maven: org.mybatis:mybatis-spring:2.0.6" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-autoconfigure:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba:druid-spring-boot-starter:1.2.8" level="project" />
+    <orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.7.30" level="project" />
+    <orderEntry type="library" name="Maven: mysql:mysql-connector-java:8.0.22" level="project" />
+    <orderEntry type="library" name="Maven: com.google.protobuf:protobuf-java:3.14.0" level="project" />
+    <orderEntry type="library" name="Maven: com.oracle:ojdbc7:12.2.0.1" level="project" />
+    <orderEntry type="library" name="Maven: org.postgresql:postgresql:42.2.22" level="project" />
+    <orderEntry type="library" name="Maven: org.checkerframework:checker-qual:3.5.0" level="project" />
+    <orderEntry type="library" name="Maven: com.microsoft.sqlserver:mssql-jdbc:8.4.1.jre8" level="project" />
+    <orderEntry type="library" name="Maven: com.dameng:DmJdbcDriver18:8.1.2.79" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-aop:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-aop:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.aspectj:aspectjweaver:1.9.6" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.retry:spring-retry:1.2.5.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-core-cloud:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-core-context:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-starter-auth:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: io.github.openfeign:feign-okhttp:10.12" level="project" />
+    <orderEntry type="library" name="Maven: io.github.openfeign:feign-core:10.12" level="project" />
+    <orderEntry type="library" name="Maven: com.squareup.okhttp3:okhttp:4.9.3" level="project" />
+    <orderEntry type="library" name="Maven: com.squareup.okio:okio:2.8.0" level="project" />
+    <orderEntry type="library" name="Maven: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.72" level="project" />
+    <orderEntry type="library" name="Maven: org.jetbrains.kotlin:kotlin-stdlib:1.3.72" level="project" />
+    <orderEntry type="library" name="Maven: org.jetbrains:annotations:13.0" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-starter-openfeign:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-openfeign-core:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: io.github.openfeign.form:feign-form-spring:3.8.0" level="project" />
+    <orderEntry type="library" name="Maven: io.github.openfeign.form:feign-form:3.8.0" level="project" />
+    <orderEntry type="library" name="Maven: commons-fileupload:commons-fileupload:1.3.3" level="project" />
+    <orderEntry type="library" name="Maven: commons-io:commons-io:2.6" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-web:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-commons:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.security:spring-security-crypto:5.3.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: io.github.openfeign:feign-slf4j:10.12" level="project" />
+    <orderEntry type="library" name="Maven: io.github.openfeign:feign-hystrix:10.12" level="project" />
+    <orderEntry type="library" name="Maven: com.netflix.archaius:archaius-core:0.7.7" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-starter-netflix-hystrix:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-netflix-hystrix:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-netflix-ribbon:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-netflix-archaius:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-starter-netflix-archaius:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: commons-configuration:commons-configuration:1.8" level="project" />
+    <orderEntry type="library" name="Maven: com.netflix.hystrix:hystrix-core:1.5.18" level="project" />
+    <orderEntry type="library" name="Maven: com.netflix.hystrix:hystrix-serialization:1.5.18" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: com.fasterxml.jackson.module:jackson-module-afterburner:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: com.netflix.hystrix:hystrix-metrics-event-stream:1.5.18" level="project" />
+    <orderEntry type="library" name="Maven: com.netflix.hystrix:hystrix-javanica:1.5.18" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: org.apache.commons:commons-lang3:3.10" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: org.ow2.asm:asm:5.0.4" level="project" />
+    <orderEntry type="library" name="Maven: io.reactivex:rxjava-reactive-streams:1.2.1" level="project" />
+    <orderEntry type="library" name="Maven: org.reactivestreams:reactive-streams:1.0.3" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-actuator:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-actuator-autoconfigure:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-actuator:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: de.codecentric:spring-boot-admin-starter-client:2.3.1" level="project" />
+    <orderEntry type="library" name="Maven: de.codecentric:spring-boot-admin-client:2.3.1" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-discovery:2.2.7.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.cloud:spring-cloud-alibaba-commons:2.2.7.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.spring:spring-context-support:1.0.10" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-context:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-config:2.2.7.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.nacos:nacos-client:2.0.4" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-core:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-databind:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: org.apache.httpcomponents:httpasyncclient:4.1.4" level="project" />
+    <orderEntry type="library" name="Maven: org.apache.httpcomponents:httpcore-nio:4.4.14" level="project" />
+    <orderEntry type="library" name="Maven: org.reflections:reflections:0.9.11" level="project" />
+    <orderEntry type="library" name="Maven: org.javassist:javassist:3.21.0-GA" level="project" />
+    <orderEntry type="library" name="Maven: io.prometheus:simpleclient:0.5.0" level="project" />
+    <orderEntry type="library" name="Maven: org.yaml:snakeyaml:1.26" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.cloud:spring-cloud-starter-alibaba-sentinel:2.2.7.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.csp:sentinel-transport-simple-http:1.8.1" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.csp:sentinel-transport-common:1.8.1" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.csp:sentinel-datasource-extension:1.8.1" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.csp:sentinel-annotation-aspectj:1.8.1" level="project" />
+    <orderEntry type="library" name="Maven: org.aspectj:aspectjrt:1.9.6" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.cloud:spring-cloud-circuitbreaker-sentinel:2.2.7.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.csp:sentinel-reactor-adapter:1.8.1" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.csp:sentinel-spring-webflux-adapter:1.8.1" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.csp:sentinel-spring-webmvc-adapter:1.8.1" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.csp:sentinel-parameter-flow-control:1.8.1" level="project" />
+    <orderEntry type="library" name="Maven: com.googlecode.concurrentlinkedhashmap:concurrentlinkedhashmap-lru:1.4.2" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.csp:sentinel-cluster-server-default:1.8.1" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.csp:sentinel-cluster-common-default:1.8.1" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.csp:sentinel-cluster-client-default:1.8.1" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.cloud:spring-cloud-alibaba-sentinel-datasource:2.2.7.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-starter-metrics:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: io.micrometer:micrometer-core:1.6.3" level="project" />
+    <orderEntry type="library" name="Maven: org.hdrhistogram:HdrHistogram:2.1.12" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: org.latencyutils:LatencyUtils:2.0.3" level="project" />
+    <orderEntry type="library" name="Maven: io.micrometer:micrometer-registry-prometheus:1.6.3" level="project" />
+    <orderEntry type="library" name="Maven: io.prometheus:simpleclient_common:0.9.0" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.csp:sentinel-core:1.8.3" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba:druid:1.2.8" level="project" />
+    <orderEntry type="library" name="Maven: javax.annotation:javax.annotation-api:1.3.2" level="project" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-starter-redis:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-starter-jwt:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: io.jsonwebtoken:jjwt-impl:0.11.2" level="project" />
+    <orderEntry type="library" name="Maven: io.jsonwebtoken:jjwt-api:0.11.2" level="project" />
+    <orderEntry type="library" name="Maven: io.jsonwebtoken:jjwt-jackson:0.11.2" level="project" />
+    <orderEntry type="library" name="Maven: org.redisson:redisson:3.11.6" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-common:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-codec:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-buffer:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-transport:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-resolver:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-resolver-dns:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-codec-dns:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-handler:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: javax.cache:cache-api:1.1.1" level="project" />
+    <orderEntry type="library" name="Maven: io.projectreactor:reactor-core:3.3.17.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: io.reactivex.rxjava2:rxjava:2.2.21" level="project" />
+    <orderEntry type="library" name="Maven: de.ruedigermoeller:fst:2.57" level="project" />
+    <orderEntry type="library" name="Maven: org.objenesis:objenesis:2.6" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: net.bytebuddy:byte-buddy:1.10.22" level="project" />
+    <orderEntry type="library" name="Maven: org.jodd:jodd-bean:5.0.13" level="project" />
+    <orderEntry type="library" name="Maven: org.jodd:jodd-core:5.0.13" level="project" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-starter-swagger:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.github.xiaoymin:knife4j-micro-spring-boot-starter:2.0.9" level="project" />
+    <orderEntry type="library" name="Maven: com.github.xiaoymin:knife4j-spring-boot-autoconfigure:2.0.9" level="project" />
+    <orderEntry type="library" name="Maven: com.github.xiaoymin:knife4j-spring:2.0.9" level="project" />
+    <orderEntry type="library" name="Maven: com.github.xiaoymin:knife4j-annotations:2.0.9" level="project" />
+    <orderEntry type="library" name="Maven: com.github.xiaoymin:knife4j-core:2.0.9" level="project" />
+    <orderEntry type="library" name="Maven: io.springfox:springfox-bean-validators:2.10.5" level="project" />
+    <orderEntry type="library" name="Maven: io.springfox:springfox-spring-webmvc:2.10.5" level="project" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-starter-social:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: me.zhyd.oauth:JustAuth:1.16.5" level="project" />
+    <orderEntry type="library" name="Maven: com.xkcoding.http:simple-http:1.0.5" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba:fastjson:1.2.80" level="project" />
+    <orderEntry type="library" name="Maven: org.apache.httpcomponents:httpclient:4.5.13" level="project" />
+    <orderEntry type="library" name="Maven: org.apache.httpcomponents:httpcore:4.4.14" level="project" />
+    <orderEntry type="module" module-name="blade-user-api" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-starter-cache:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-cache:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-starter-mybatis:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.mybatis:mybatis-typehandlers-jsr310:1.0.2" level="project" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-starter-tenant:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.baomidou:dynamic-datasource-spring-boot-starter:3.3.6" level="project" />
+    <orderEntry type="library" name="Maven: io.springfox:springfox-swagger2:2.10.5" level="project" />
+    <orderEntry type="library" name="Maven: io.swagger:swagger-annotations:1.5.20" level="project" />
+    <orderEntry type="library" name="Maven: io.springfox:springfox-spi:2.10.5" level="project" />
+    <orderEntry type="library" name="Maven: io.springfox:springfox-core:2.10.5" level="project" />
+    <orderEntry type="library" name="Maven: io.springfox:springfox-schema:2.10.5" level="project" />
+    <orderEntry type="library" name="Maven: io.springfox:springfox-swagger-common:2.10.5" level="project" />
+    <orderEntry type="library" name="Maven: io.springfox:springfox-spring-web:2.10.5" level="project" />
+    <orderEntry type="library" name="Maven: io.github.classgraph:classgraph:4.1.7" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml:classmate:1.5.1" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.plugin:spring-plugin-core:2.0.0.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.plugin:spring-plugin-metadata:2.0.0.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.mapstruct:mapstruct:1.3.1.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.swagger:swagger-models:1.6.2" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.11.4" level="project" />
+    <orderEntry type="module" module-name="blade-system-api" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-starter-security:2.2.5.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-starter:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.security:spring-security-rsa:1.0.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-security:2.2.5.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-security:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: javax.activation:javax.activation-api:1.2.0" level="project" />
+    <orderEntry type="library" name="Maven: javax.xml.bind:jaxb-api:2.3.1" level="project" />
+    <orderEntry type="library" name="Maven: org.glassfish.jaxb:jaxb-runtime:2.3.4" level="project" />
+    <orderEntry type="library" name="Maven: jakarta.xml.bind:jakarta.xml.bind-api:2.3.3" level="project" />
+    <orderEntry type="library" name="Maven: org.glassfish.jaxb:txw2:2.3.4" level="project" />
+    <orderEntry type="library" name="Maven: com.sun.istack:istack-commons-runtime:3.0.12" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: com.sun.activation:jakarta.activation:1.2.2" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.security.oauth:spring-security-oauth2:2.3.5.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-beans:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-core:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-jcl:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-context:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-expression:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-webmvc:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.security:spring-security-core:5.3.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.security:spring-security-config:5.3.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.security:spring-security-web:5.3.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: commons-codec:commons-codec:1.14" level="project" />
+    <orderEntry type="library" name="Maven: org.codehaus.jackson:jackson-mapper-asl:1.9.13" level="project" />
+    <orderEntry type="library" name="Maven: org.codehaus.jackson:jackson-core-asl:1.9.13" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-data-redis:2.2.0.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-logging:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: ch.qos.logback:logback-classic:1.2.11" level="project" />
+    <orderEntry type="library" name="Maven: ch.qos.logback:logback-core:1.2.11" level="project" />
+    <orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-to-slf4j:2.17.2" level="project" />
+    <orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-api:2.17.2" level="project" />
+    <orderEntry type="library" name="Maven: org.slf4j:jul-to-slf4j:1.7.30" level="project" />
+    <orderEntry type="library" name="Maven: jakarta.annotation:jakarta.annotation-api:1.3.5" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.data:spring-data-redis:2.3.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.data:spring-data-keyvalue:2.3.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.data:spring-data-commons:2.3.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-tx:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-oxm:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-context-support:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: io.lettuce:lettuce-core:5.3.7.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.security:spring-security-jwt:1.0.10.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.bouncycastle:bcpkix-jdk15on:1.59" level="project" />
+    <orderEntry type="library" name="Maven: org.bouncycastle:bcprov-jdk15on:1.59" level="project" />
+    <orderEntry type="library" name="Maven: com.github.whvcse:easy-captcha:1.6.2" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.projectlombok:lombok:1.18.22" level="project" />
+  </component>
+</module>

+ 140 - 0
blade-auth/pom.xml

@@ -0,0 +1,140 @@
+<?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">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <artifactId>BladeX</artifactId>
+        <groupId>org.springblade</groupId>
+        <version>2.9.1.RELEASE</version>
+    </parent>
+
+    <artifactId>blade-auth</artifactId>
+    <name>${project.artifactId}</name>
+    <version>${bladex.project.version}</version>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <!--Blade-->
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-common</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.springblade</groupId>
+                    <artifactId>blade-scope-api</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-core-db</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-core-cloud</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-starter-metrics</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-starter-redis</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-starter-swagger</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-starter-social</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-user-api</artifactId>
+            <version>${bladex.project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-system-api</artifactId>
+            <version>${bladex.project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-security</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.security.oauth</groupId>
+            <artifactId>spring-security-oauth2</artifactId>
+            <version>2.3.5.RELEASE</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis</artifactId>
+            <version>2.2.0.RELEASE</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.security</groupId>
+            <artifactId>spring-security-jwt</artifactId>
+        </dependency>
+        <!-- 验证码 -->
+        <dependency>
+            <groupId>com.github.whvcse</groupId>
+            <artifactId>easy-captcha</artifactId>
+        </dependency>
+        <!-- 链路追踪、服务监控 -->
+        <!--<dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-starter-trace</artifactId>
+        </dependency>-->
+        <!-- 解决Java11无法运行的问题 -->
+        <!--<dependency>
+            <groupId>javax.xml.bind</groupId>
+            <artifactId>jaxb-api</artifactId>
+            <version>2.2.11</version>
+        </dependency>
+        <dependency>
+            <groupId>com.sun.xml.bind</groupId>
+            <artifactId>jaxb-core</artifactId>
+            <version>2.2.11</version>
+        </dependency>
+        <dependency>
+            <groupId>com.sun.xml.bind</groupId>
+            <artifactId>jaxb-impl</artifactId>
+            <version>2.2.11</version>
+        </dependency>
+        <dependency>
+            <groupId>javax.activation</groupId>
+            <artifactId>activation</artifactId>
+            <version>1.1.1</version>
+        </dependency>-->
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>com.spotify</groupId>
+                <artifactId>dockerfile-maven-plugin</artifactId>
+                <configuration>
+                    <username>${docker.username}</username>
+                    <password>${docker.password}</password>
+                    <repository>${docker.registry.url}/${docker.namespace}/${project.artifactId}</repository>
+                    <tag>${project.version}</tag>
+                    <useMavenSettingsForAuth>true</useMavenSettingsForAuth>
+                    <buildArgs>
+                        <JAR_FILE>target/${project.build.finalName}.jar</JAR_FILE>
+                    </buildArgs>
+                    <skip>false</skip>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-antrun-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 38 - 0
blade-auth/src/main/java/org/springblade/auth/AuthApplication.java

@@ -0,0 +1,38 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.auth;
+
+
+import org.springblade.core.cloud.feign.EnableBladeFeign;
+import org.springblade.core.launch.BladeApplication;
+import org.springblade.core.launch.constant.AppConstant;
+import org.springframework.cloud.client.SpringCloudApplication;
+
+/**
+ * 用户认证服务器
+ *
+ * @author Chill
+ */
+@EnableBladeFeign
+@SpringCloudApplication
+public class AuthApplication {
+
+	public static void main(String[] args) {
+		BladeApplication.run(AppConstant.APPLICATION_AUTH_NAME, AuthApplication.class, args);
+	}
+
+}

+ 117 - 0
blade-auth/src/main/java/org/springblade/auth/config/BladeAuthorizationServerConfiguration.java

@@ -0,0 +1,117 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.auth.config;
+
+import lombok.AllArgsConstructor;
+import lombok.SneakyThrows;
+import org.springblade.auth.constant.AuthConstant;
+import org.springblade.auth.granter.BladeTokenGranter;
+import org.springblade.auth.service.BladeClientDetailsServiceImpl;
+import org.springblade.core.redis.cache.BladeRedis;
+import org.springblade.core.social.props.SocialProperties;
+import org.springblade.system.user.feign.IUserClient;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.annotation.Order;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
+import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
+import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
+import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
+import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
+import org.springframework.security.oauth2.provider.TokenGranter;
+import org.springframework.security.oauth2.provider.token.TokenEnhancer;
+import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
+import org.springframework.security.oauth2.provider.token.TokenStore;
+import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
+
+import javax.sql.DataSource;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 认证服务器配置
+ *
+ * @author Chill
+ */
+@Order
+@Configuration
+@AllArgsConstructor
+@EnableAuthorizationServer
+public class BladeAuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
+
+	private final DataSource dataSource;
+
+	private final AuthenticationManager authenticationManager;
+
+	private final UserDetailsService userDetailsService;
+
+	private final TokenStore tokenStore;
+
+	private final TokenEnhancer jwtTokenEnhancer;
+
+	private final JwtAccessTokenConverter jwtAccessTokenConverter;
+
+	private final BladeRedis bladeRedis;
+
+	private final IUserClient userClient;
+
+	private final SocialProperties socialProperties;
+
+	@Override
+	public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
+		//获取自定义tokenGranter
+		TokenGranter tokenGranter = BladeTokenGranter.getTokenGranter(authenticationManager, endpoints, bladeRedis, userClient, socialProperties);
+
+		//配置端点
+		endpoints.tokenStore(tokenStore)
+			.authenticationManager(authenticationManager)
+			.userDetailsService(userDetailsService)
+			.tokenGranter(tokenGranter);
+
+		//扩展token返回结果
+		if (jwtAccessTokenConverter != null && jwtTokenEnhancer != null) {
+			TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
+			List<TokenEnhancer> enhancerList = new ArrayList<>();
+			enhancerList.add(jwtTokenEnhancer);
+			enhancerList.add(jwtAccessTokenConverter);
+			tokenEnhancerChain.setTokenEnhancers(enhancerList);
+			//jwt增强
+			endpoints.tokenEnhancer(tokenEnhancerChain).accessTokenConverter(jwtAccessTokenConverter);
+		}
+	}
+
+	/**
+	 * 配置客户端信息
+	 */
+	@Override
+	@SneakyThrows
+	public void configure(ClientDetailsServiceConfigurer clients) {
+		BladeClientDetailsServiceImpl clientDetailsService = new BladeClientDetailsServiceImpl(dataSource);
+		clientDetailsService.setSelectClientDetailsSql(AuthConstant.DEFAULT_SELECT_STATEMENT);
+		clientDetailsService.setFindClientDetailsSql(AuthConstant.DEFAULT_FIND_STATEMENT);
+		clients.withClientDetails(clientDetailsService);
+	}
+
+	@Override
+	public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
+		oauthServer
+			.allowFormAuthenticationForClients()
+			.tokenKeyAccess("permitAll()")
+			.checkTokenAccess("isAuthenticated()");
+	}
+}

+ 59 - 0
blade-auth/src/main/java/org/springblade/auth/config/BladeResourceServerConfiguration.java

@@ -0,0 +1,59 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.auth.config;
+
+import lombok.AllArgsConstructor;
+import lombok.SneakyThrows;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
+import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
+
+/**
+ * 自定义登录成功配置
+ *
+ * @author Chill
+ */
+@Configuration
+@AllArgsConstructor
+@EnableResourceServer
+public class BladeResourceServerConfiguration extends ResourceServerConfigurerAdapter {
+
+	@Override
+	@SneakyThrows
+	public void configure(HttpSecurity http) {
+		http.headers().frameOptions().disable();
+		http.formLogin()
+			.and()
+			.authorizeRequests()
+			.antMatchers(
+				"/actuator/**",
+				"/oauth/captcha",
+				"/oauth/logout",
+				"/oauth/clear-cache",
+				"/oauth/render/**",
+				"/oauth/callback/**",
+				"/oauth/revoke/**",
+				"/oauth/refresh/**",
+				"/token/**",
+				"/mobile/**",
+				"/v2/api-docs").permitAll()
+			.anyRequest().authenticated().and()
+			.csrf().disable();
+	}
+
+}

+ 66 - 0
blade-auth/src/main/java/org/springblade/auth/config/JwtTokenStoreConfiguration.java

@@ -0,0 +1,66 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.auth.config;
+
+import org.springblade.auth.support.BladeJwtTokenEnhancer;
+import org.springblade.core.jwt.props.JwtProperties;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.oauth2.provider.token.TokenEnhancer;
+import org.springframework.security.oauth2.provider.token.TokenStore;
+import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
+import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
+
+/**
+ * JwtTokenStore
+ *
+ * @author Chill
+ */
+@Configuration
+@ConditionalOnProperty(prefix = "blade.security.oauth2", name = "storeType", havingValue = "jwt", matchIfMissing = true)
+public class JwtTokenStoreConfiguration {
+
+	/**
+	 * 使用jwtTokenStore存储token
+	 */
+	@Bean
+	public TokenStore jwtTokenStore(JwtProperties jwtProperties) {
+		return new JwtTokenStore(jwtAccessTokenConverter(jwtProperties));
+	}
+
+	/**
+	 * 用于生成jwt
+	 */
+	@Bean
+	public JwtAccessTokenConverter jwtAccessTokenConverter(JwtProperties jwtProperties) {
+		JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
+		accessTokenConverter.setSigningKey(jwtProperties.getSignKey());
+		return accessTokenConverter;
+	}
+
+	/**
+	 * 用于扩展jwt
+	 */
+	@Bean
+	@ConditionalOnMissingBean(name = "jwtTokenEnhancer")
+	public TokenEnhancer jwtTokenEnhancer(JwtAccessTokenConverter jwtAccessTokenConverter, JwtProperties jwtProperties) {
+		return new BladeJwtTokenEnhancer(jwtAccessTokenConverter, jwtProperties);
+	}
+
+}

+ 56 - 0
blade-auth/src/main/java/org/springblade/auth/config/SecurityConfiguration.java

@@ -0,0 +1,56 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.auth.config;
+
+import lombok.AllArgsConstructor;
+import lombok.SneakyThrows;
+import org.springblade.auth.support.BladePasswordEncoderFactories;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.crypto.password.PasswordEncoder;
+
+/**
+ * Security配置
+ *
+ * @author Chill
+ */
+@Configuration
+@AllArgsConstructor
+public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
+
+	@Bean
+	@Override
+	@SneakyThrows
+	public AuthenticationManager authenticationManagerBean() {
+		return super.authenticationManagerBean();
+	}
+
+	@Bean
+	public PasswordEncoder passwordEncoder() {
+		return BladePasswordEncoderFactories.createDelegatingPasswordEncoder();
+	}
+
+	@Override
+	@SneakyThrows
+	protected void configure(HttpSecurity http) {
+		http.httpBasic().and().csrf().disable().authorizeRequests().anyRequest().fullyAuthenticated();
+	}
+
+}

+ 53 - 0
blade-auth/src/main/java/org/springblade/auth/constant/AuthConstant.java

@@ -0,0 +1,53 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.auth.constant;
+
+/**
+ * 授权校验常量
+ *
+ * @author Chill
+ */
+public interface AuthConstant {
+
+	/**
+	 * 密码加密规则
+	 */
+	String ENCRYPT = "{blade}";
+
+	/**
+	 * blade_client表字段
+	 */
+	String CLIENT_FIELDS = "client_id, CONCAT('{noop}',client_secret) as client_secret, resource_ids, scope, authorized_grant_types, " +
+		"web_server_redirect_uri, authorities, access_token_validity, " +
+		"refresh_token_validity, additional_information, autoapprove";
+
+	/**
+	 * blade_client查询语句
+	 */
+	String BASE_STATEMENT = "select " + CLIENT_FIELDS + " from blade_client";
+
+	/**
+	 * blade_client查询排序
+	 */
+	String DEFAULT_FIND_STATEMENT = BASE_STATEMENT + " order by client_id";
+
+	/**
+	 * 查询client_id
+	 */
+	String DEFAULT_SELECT_STATEMENT = BASE_STATEMENT + " where client_id = ?";
+
+}

+ 88 - 0
blade-auth/src/main/java/org/springblade/auth/endpoint/BladeSocialEndpoint.java

@@ -0,0 +1,88 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.auth.endpoint;
+
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import me.zhyd.oauth.model.AuthCallback;
+import me.zhyd.oauth.model.AuthToken;
+import me.zhyd.oauth.request.AuthRequest;
+import me.zhyd.oauth.utils.AuthStateUtils;
+import org.springblade.core.social.props.SocialProperties;
+import org.springblade.core.social.utils.SocialUtil;
+import org.springblade.core.tenant.annotation.NonDS;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * SocialEndpoint
+ *
+ * @author Chill
+ */
+@NonDS
+@Slf4j
+@RestController
+@AllArgsConstructor
+@ConditionalOnProperty(value = "social.enabled", havingValue = "true")
+public class BladeSocialEndpoint {
+
+	private final SocialProperties socialProperties;
+
+	/**
+	 * 授权完毕跳转
+	 */
+	@RequestMapping("/oauth/render/{source}")
+	public void renderAuth(@PathVariable("source") String source, HttpServletResponse response) throws IOException {
+		AuthRequest authRequest = SocialUtil.getAuthRequest(source, socialProperties);
+		String authorizeUrl = authRequest.authorize(AuthStateUtils.createState());
+		response.sendRedirect(authorizeUrl);
+	}
+
+	/**
+	 * 获取认证信息
+	 */
+	@RequestMapping("/oauth/callback/{source}")
+	public Object login(@PathVariable("source") String source, AuthCallback callback) {
+		AuthRequest authRequest = SocialUtil.getAuthRequest(source, socialProperties);
+		return authRequest.login(callback);
+	}
+
+	/**
+	 * 撤销授权
+	 */
+	@RequestMapping("/oauth/revoke/{source}/{token}")
+	public Object revokeAuth(@PathVariable("source") String source, @PathVariable("token") String token) {
+		AuthRequest authRequest = SocialUtil.getAuthRequest(source, socialProperties);
+		return authRequest.revoke(AuthToken.builder().accessToken(token).build());
+	}
+
+	/**
+	 * 续期令牌
+	 */
+	@RequestMapping("/oauth/refresh/{source}")
+	public Object refreshAuth(@PathVariable("source") String source, String token) {
+		AuthRequest authRequest = SocialUtil.getAuthRequest(source, socialProperties);
+		return authRequest.refresh(AuthToken.builder().refreshToken(token).build());
+	}
+
+
+}

+ 100 - 0
blade-auth/src/main/java/org/springblade/auth/endpoint/BladeTokenEndPoint.java

@@ -0,0 +1,100 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.auth.endpoint;
+
+import com.wf.captcha.SpecCaptcha;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springblade.common.cache.CacheNames;
+import org.springblade.core.cache.utils.CacheUtil;
+import org.springblade.core.jwt.JwtUtil;
+import org.springblade.core.jwt.props.JwtProperties;
+import org.springblade.core.launch.constant.TokenConstant;
+import org.springblade.core.redis.cache.BladeRedis;
+import org.springblade.core.secure.BladeUser;
+import org.springblade.core.secure.utils.AuthUtil;
+import org.springblade.core.tenant.annotation.NonDS;
+import org.springblade.core.tool.api.R;
+import org.springblade.core.tool.support.Kv;
+import org.springblade.core.tool.utils.StringUtil;
+import org.springblade.core.tool.utils.WebUtil;
+import org.springframework.security.core.Authentication;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.time.Duration;
+
+import static org.springblade.core.cache.constant.CacheConstant.*;
+
+/**
+ * BladeEndPoint
+ *
+ * @author Chill
+ */
+@NonDS
+@Slf4j
+@RestController
+@AllArgsConstructor
+public class BladeTokenEndPoint {
+
+	private final BladeRedis bladeRedis;
+	private final JwtProperties jwtProperties;
+
+	@GetMapping("/oauth/user-info")
+	public R<Authentication> currentUser(Authentication authentication) {
+		return R.data(authentication);
+	}
+
+	@GetMapping("/oauth/captcha")
+	public Kv captcha() {
+		SpecCaptcha specCaptcha = new SpecCaptcha(130, 48, 5);
+		String verCode = specCaptcha.text().toLowerCase();
+		String key = StringUtil.randomUUID();
+		// 存入redis并设置过期时间为30分钟
+		bladeRedis.setEx(CacheNames.CAPTCHA_KEY + key, verCode, Duration.ofMinutes(30));
+		// 将key和base64返回给前端
+		return Kv.create().set("key", key).set("image", specCaptcha.toBase64());
+	}
+
+	@GetMapping("/oauth/logout")
+	public Kv logout() {
+		BladeUser user = AuthUtil.getUser();
+		if (user != null && jwtProperties.getState()) {
+			String token = JwtUtil.getToken(WebUtil.getRequest().getHeader(TokenConstant.HEADER));
+			JwtUtil.removeAccessToken(user.getTenantId(), String.valueOf(user.getUserId()), token);
+		}
+		return Kv.create().set("success", "true").set("msg", "success");
+	}
+
+	@GetMapping("/oauth/clear-cache")
+	public Kv clearCache() {
+		CacheUtil.clear(BIZ_CACHE);
+		CacheUtil.clear(USER_CACHE);
+		CacheUtil.clear(DICT_CACHE);
+		CacheUtil.clear(FLOW_CACHE);
+		CacheUtil.clear(SYS_CACHE);
+		CacheUtil.clear(PARAM_CACHE);
+		CacheUtil.clear(RESOURCE_CACHE);
+		CacheUtil.clear(MENU_CACHE);
+		CacheUtil.clear(DICT_CACHE, Boolean.FALSE);
+		CacheUtil.clear(MENU_CACHE, Boolean.FALSE);
+		CacheUtil.clear(SYS_CACHE, Boolean.FALSE);
+		CacheUtil.clear(PARAM_CACHE, Boolean.FALSE);
+		return Kv.create().set("success", "true").set("msg", "success");
+	}
+
+}

+ 52 - 0
blade-auth/src/main/java/org/springblade/auth/granter/BladeTokenGranter.java

@@ -0,0 +1,52 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.auth.granter;
+
+import org.springblade.core.redis.cache.BladeRedis;
+import org.springblade.core.social.props.SocialProperties;
+import org.springblade.system.user.feign.IUserClient;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
+import org.springframework.security.oauth2.provider.CompositeTokenGranter;
+import org.springframework.security.oauth2.provider.TokenGranter;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * 自定义拓展TokenGranter
+ *
+ * @author Chill
+ */
+public class BladeTokenGranter {
+
+	/**
+	 * 自定义tokenGranter
+	 */
+	public static TokenGranter getTokenGranter(final AuthenticationManager authenticationManager, final AuthorizationServerEndpointsConfigurer endpoints, BladeRedis bladeRedis, IUserClient userClient, SocialProperties socialProperties) {
+		// 默认tokenGranter集合
+		List<TokenGranter> granters = new ArrayList<>(Collections.singletonList(endpoints.getTokenGranter()));
+		// 增加验证码模式
+		granters.add(new CaptchaTokenGranter(authenticationManager, endpoints.getTokenServices(), endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory(), bladeRedis));
+		// 增加第三方登陆模式
+		granters.add(new SocialTokenGranter(endpoints.getTokenServices(), endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory(), userClient, socialProperties));
+		// 组合tokenGranter集合
+		return new CompositeTokenGranter(granters);
+	}
+
+}

+ 82 - 0
blade-auth/src/main/java/org/springblade/auth/granter/CaptchaTokenGranter.java

@@ -0,0 +1,82 @@
+package org.springblade.auth.granter;
+
+import org.springblade.auth.utils.TokenUtil;
+import org.springblade.common.cache.CacheNames;
+import org.springblade.core.redis.cache.BladeRedis;
+import org.springblade.core.tool.utils.StringUtil;
+import org.springblade.core.tool.utils.WebUtil;
+import org.springframework.security.authentication.*;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
+import org.springframework.security.oauth2.common.exceptions.UserDeniedAuthorizationException;
+import org.springframework.security.oauth2.provider.*;
+import org.springframework.security.oauth2.provider.token.AbstractTokenGranter;
+import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * 验证码TokenGranter
+ *
+ * @author Chill
+ */
+public class CaptchaTokenGranter extends AbstractTokenGranter {
+
+	private static final String GRANT_TYPE = "captcha";
+
+	private final AuthenticationManager authenticationManager;
+
+	private BladeRedis bladeRedis;
+
+	public CaptchaTokenGranter(AuthenticationManager authenticationManager,
+							   AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory, BladeRedis bladeRedis) {
+		this(authenticationManager, tokenServices, clientDetailsService, requestFactory, GRANT_TYPE);
+		this.bladeRedis = bladeRedis;
+	}
+
+	protected CaptchaTokenGranter(AuthenticationManager authenticationManager, AuthorizationServerTokenServices tokenServices,
+												ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory, String grantType) {
+		super(tokenServices, clientDetailsService, requestFactory, grantType);
+		this.authenticationManager = authenticationManager;
+	}
+
+	@Override
+	protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
+		HttpServletRequest request = WebUtil.getRequest();
+		// 增加验证码判断
+		String key = request.getHeader(TokenUtil.CAPTCHA_HEADER_KEY);
+		String code = request.getHeader(TokenUtil.CAPTCHA_HEADER_CODE);
+		// 获取验证码
+		String redisCode = bladeRedis.get(CacheNames.CAPTCHA_KEY + key);
+		// 判断验证码
+		if (code == null || !StringUtil.equalsIgnoreCase(redisCode, code)) {
+			throw new UserDeniedAuthorizationException(TokenUtil.CAPTCHA_NOT_CORRECT);
+		}
+
+		Map<String, String> parameters = new LinkedHashMap<String, String>(tokenRequest.getRequestParameters());
+		String username = parameters.get("username");
+		String password = parameters.get("password");
+		// Protect from downstream leaks of password
+		parameters.remove("password");
+
+		Authentication userAuth = new UsernamePasswordAuthenticationToken(username, password);
+		((AbstractAuthenticationToken) userAuth).setDetails(parameters);
+		try {
+			userAuth = authenticationManager.authenticate(userAuth);
+		}
+		catch (AccountStatusException | BadCredentialsException ase) {
+			//covers expired, locked, disabled cases (mentioned in section 5.2, draft 31)
+			throw new InvalidGrantException(ase.getMessage());
+		}
+		// If the username/password are wrong the spec says we should send 400/invalid grant
+
+		if (userAuth == null || !userAuth.isAuthenticated()) {
+			throw new InvalidGrantException("Could not authenticate user: " + username);
+		}
+
+		OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);
+		return new OAuth2Authentication(storedOAuth2Request, userAuth);
+	}
+}

+ 130 - 0
blade-auth/src/main/java/org/springblade/auth/granter/SocialTokenGranter.java

@@ -0,0 +1,130 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.auth.granter;
+
+import me.zhyd.oauth.model.AuthCallback;
+import me.zhyd.oauth.model.AuthResponse;
+import me.zhyd.oauth.model.AuthUser;
+import me.zhyd.oauth.request.AuthRequest;
+import org.springblade.auth.constant.AuthConstant;
+import org.springblade.auth.service.BladeUserDetails;
+import org.springblade.auth.utils.TokenUtil;
+import org.springblade.core.social.props.SocialProperties;
+import org.springblade.core.social.utils.SocialUtil;
+import org.springblade.core.tool.api.R;
+import org.springblade.core.tool.support.Kv;
+import org.springblade.core.tool.utils.BeanUtil;
+import org.springblade.core.tool.utils.Func;
+import org.springblade.core.tool.utils.WebUtil;
+import org.springblade.system.user.entity.User;
+import org.springblade.system.user.entity.UserInfo;
+import org.springblade.system.user.entity.UserOauth;
+import org.springblade.system.user.feign.IUserClient;
+import org.springframework.security.authentication.AbstractAuthenticationToken;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.authority.AuthorityUtils;
+import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
+import org.springframework.security.oauth2.provider.*;
+import org.springframework.security.oauth2.provider.token.AbstractTokenGranter;
+import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * 第三方登录认证类
+ *
+ * @author Chill
+ */
+public class SocialTokenGranter extends AbstractTokenGranter {
+	private static final String GRANT_TYPE = "social";
+	private static final Integer AUTH_SUCCESS_CODE = 2000;
+
+	private final IUserClient userClient;
+	private final SocialProperties socialProperties;
+
+	protected SocialTokenGranter(AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory, IUserClient userClient, SocialProperties socialProperties) {
+		super(tokenServices, clientDetailsService, requestFactory, GRANT_TYPE);
+		this.userClient = userClient;
+		this.socialProperties = socialProperties;
+	}
+
+	@Override
+	protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
+		// 请求头租户信息
+		HttpServletRequest request = WebUtil.getRequest();
+		String tenantId = Func.toStr(request.getHeader(TokenUtil.TENANT_HEADER_KEY), TokenUtil.DEFAULT_TENANT_ID);
+
+		Map<String, String> parameters = new LinkedHashMap<>(tokenRequest.getRequestParameters());
+		// 开放平台来源
+		String sourceParameter = parameters.get("source");
+		// 匹配是否有别名定义
+		String source = socialProperties.getAlias().getOrDefault(sourceParameter, sourceParameter);
+		// 开放平台授权码
+		String code = parameters.get("code");
+		// 开放平台状态吗
+		String state = parameters.get("state");
+
+		// 获取开放平台授权数据
+		AuthRequest authRequest = SocialUtil.getAuthRequest(source, socialProperties);
+		AuthCallback authCallback = new AuthCallback();
+		authCallback.setCode(code);
+		authCallback.setState(state);
+		AuthResponse authResponse = authRequest.login(authCallback);
+		AuthUser authUser;
+		if (authResponse.getCode() == AUTH_SUCCESS_CODE) {
+			authUser = (AuthUser) authResponse.getData();
+		} else {
+			throw new InvalidGrantException("social grant failure, auth response is not success");
+		}
+
+		// 组装数据
+		UserOauth userOauth = Objects.requireNonNull(BeanUtil.copy(authUser, UserOauth.class));
+		userOauth.setSource(authUser.getSource());
+		userOauth.setTenantId(tenantId);
+		userOauth.setUuid(authUser.getUuid());
+
+		// 远程调用,获取认证信息
+		R<UserInfo> result = userClient.userAuthInfo(userOauth);
+		BladeUserDetails bladeUserDetails;
+		if (result.isSuccess()) {
+			User user = result.getData().getUser();
+			Kv detail = result.getData().getDetail();
+			if (user == null || user.getId() == null) {
+				throw new InvalidGrantException("social grant failure, user is null");
+			}
+			bladeUserDetails = new BladeUserDetails(user.getId(),
+				tenantId, result.getData().getOauthId(), user.getName(), user.getRealName(), user.getDeptId(), user.getPostId(), user.getRoleId(), Func.join(result.getData().getRoles()), Func.toStr(userOauth.getAvatar(), TokenUtil.DEFAULT_AVATAR),
+				userOauth.getUsername(), AuthConstant.ENCRYPT + user.getPassword(), detail, true, true, true, true,
+				AuthorityUtils.commaSeparatedStringToAuthorityList(Func.join(result.getData().getRoles())));
+		} else {
+			throw new InvalidGrantException("social grant failure, feign client return error");
+		}
+
+		// 组装认证数据,关闭密码校验
+		Authentication userAuth = new UsernamePasswordAuthenticationToken(bladeUserDetails, null, bladeUserDetails.getAuthorities());
+		((AbstractAuthenticationToken) userAuth).setDetails(parameters);
+		OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);
+
+		// 返回 OAuth2Authentication
+		return new OAuth2Authentication(storedOAuth2Request, userAuth);
+	}
+
+}

+ 51 - 0
blade-auth/src/main/java/org/springblade/auth/service/BladeClientDetailsServiceImpl.java

@@ -0,0 +1,51 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.auth.service;
+
+import org.springframework.security.oauth2.provider.ClientDetails;
+import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
+import org.springframework.stereotype.Component;
+
+import javax.sql.DataSource;
+
+/**
+ * 客户端信息
+ *
+ * @author Chill
+ */
+@Component
+public class BladeClientDetailsServiceImpl extends JdbcClientDetailsService {
+
+	public BladeClientDetailsServiceImpl(DataSource dataSource) {
+		super(dataSource);
+	}
+
+	/**
+	 * 缓存客户端信息
+	 *
+	 * @param clientId 客户端id
+	 */
+	@Override
+	public ClientDetails loadClientByClientId(String clientId) {
+		try {
+			return super.loadClientByClientId(clientId);
+		} catch (Exception ex) {
+			ex.printStackTrace();
+			return null;
+		}
+	}
+}

+ 99 - 0
blade-auth/src/main/java/org/springblade/auth/service/BladeUserDetails.java

@@ -0,0 +1,99 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.auth.service;
+
+import lombok.Getter;
+import org.springblade.core.tool.support.Kv;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.userdetails.User;
+
+import java.util.Collection;
+
+/**
+ * 用户信息拓展
+ *
+ * @author Chill
+ */
+@Getter
+public class BladeUserDetails extends User {
+
+	/**
+	 * 用户id
+	 */
+	private final Long userId;
+	/**
+	 * 租户ID
+	 */
+	private final String tenantId;
+	/**
+	 * 第三方认证ID
+	 */
+	private final String oauthId;
+	/**
+	 * 昵称
+	 */
+	private final String name;
+	/**
+	 * 真名
+	 */
+	private final String realName;
+	/**
+	 * 账号
+	 */
+	private final String account;
+	/**
+	 * 部门id
+	 */
+	private final String deptId;
+	/**
+	 * 岗位id
+	 */
+	private final String postId;
+	/**
+	 * 角色id
+	 */
+	private final String roleId;
+	/**
+	 * 角色名
+	 */
+	private final String roleName;
+	/**
+	 * 头像
+	 */
+	private final String avatar;
+	/**
+	 * 用户详情
+	 */
+	private final Kv detail;
+
+	public BladeUserDetails(Long userId, String tenantId, String oauthId, String name, String realName, String deptId, String postId, String roleId, String roleName, String avatar, String username, String password, Kv detail, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) {
+		super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
+		this.userId = userId;
+		this.tenantId = tenantId;
+		this.oauthId = oauthId;
+		this.name = name;
+		this.realName = realName;
+		this.account = username;
+		this.deptId = deptId;
+		this.postId = postId;
+		this.roleId = roleId;
+		this.roleName = roleName;
+		this.avatar = avatar;
+		this.detail = detail;
+	}
+
+}

+ 212 - 0
blade-auth/src/main/java/org/springblade/auth/service/BladeUserDetailsServiceImpl.java

@@ -0,0 +1,212 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.auth.service;
+
+import io.jsonwebtoken.Claims;
+import lombok.AllArgsConstructor;
+import lombok.SneakyThrows;
+import org.apache.commons.lang.StringUtils;
+import org.springblade.auth.constant.AuthConstant;
+import org.springblade.auth.utils.TokenUtil;
+import org.springblade.common.cache.CacheNames;
+import org.springblade.core.jwt.JwtUtil;
+import org.springblade.core.jwt.props.JwtProperties;
+import org.springblade.core.redis.cache.BladeRedis;
+import org.springblade.core.tool.api.R;
+import org.springblade.core.tool.utils.*;
+import org.springblade.system.cache.ParamCache;
+import org.springblade.system.entity.Tenant;
+import org.springblade.system.feign.ISysClient;
+import org.springblade.system.user.entity.User;
+import org.springblade.system.user.entity.UserInfo;
+import org.springblade.system.user.enums.UserEnum;
+import org.springblade.system.user.feign.IUserClient;
+import org.springframework.security.core.authority.AuthorityUtils;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.security.oauth2.common.exceptions.UserDeniedAuthorizationException;
+import org.springframework.stereotype.Service;
+
+import javax.servlet.http.HttpServletRequest;
+import java.time.Duration;
+import java.util.List;
+
+/**
+ * 用户信息
+ *
+ * @author Chill
+ */
+@Service
+@AllArgsConstructor
+public class BladeUserDetailsServiceImpl implements UserDetailsService {
+
+	public static final Integer FAIL_COUNT = 5;
+	public static final String FAIL_COUNT_VALUE = "account.failCount";
+
+	private final IUserClient userClient;
+	private final ISysClient sysClient;
+
+	private final BladeRedis bladeRedis;
+	private final JwtProperties jwtProperties;
+
+	@Override
+	@SneakyThrows
+	public BladeUserDetails loadUserByUsername(String username) {
+		HttpServletRequest request = WebUtil.getRequest();
+		// 获取用户绑定ID
+		String headerDept = request.getHeader(TokenUtil.DEPT_HEADER_KEY);
+		String headerRole = request.getHeader(TokenUtil.ROLE_HEADER_KEY);
+		// 获取租户ID
+		String headerTenant = request.getHeader(TokenUtil.TENANT_HEADER_KEY);
+		String paramTenant = request.getParameter(TokenUtil.TENANT_PARAM_KEY);
+		String password = request.getParameter(TokenUtil.PASSWORD_KEY);
+		String grantType = request.getParameter(TokenUtil.GRANT_TYPE_KEY);
+		// 判断租户请求头
+		if (StringUtil.isAllBlank(headerTenant, paramTenant)) {
+			throw new UserDeniedAuthorizationException(TokenUtil.TENANT_NOT_FOUND);
+		}
+		// 判断令牌合法性
+		if (!judgeRefreshToken(grantType, request)) {
+			throw new UserDeniedAuthorizationException(TokenUtil.TOKEN_NOT_PERMISSION);
+		}
+
+		// 指定租户ID
+		String tenantId = StringUtils.isBlank(headerTenant) ? paramTenant : headerTenant;
+		// 判断登录是否锁定
+		int count = getFailCount(tenantId, username);
+		int failCount = Func.toInt(ParamCache.getValue(FAIL_COUNT_VALUE), FAIL_COUNT);
+		if (count >= failCount) {
+			throw new UserDeniedAuthorizationException(TokenUtil.USER_HAS_TOO_MANY_FAILS);
+		}
+
+		// 获取租户信息
+		R<Tenant> tenant = sysClient.getTenant(tenantId);
+		if (tenant.isSuccess()) {
+			if (TokenUtil.judgeTenant(tenant.getData())) {
+				throw new UserDeniedAuthorizationException(TokenUtil.USER_HAS_NO_TENANT_PERMISSION);
+			}
+		} else {
+			throw new UserDeniedAuthorizationException(TokenUtil.USER_HAS_NO_TENANT);
+		}
+
+		// 获取用户类型
+		String userType = Func.toStr(request.getHeader(TokenUtil.USER_TYPE_HEADER_KEY), TokenUtil.DEFAULT_USER_TYPE);
+
+		// 远程调用返回数据
+		R<UserInfo> result;
+		// 根据不同用户类型调用对应的接口返回数据,用户可自行拓展
+		if (userType.equals(UserEnum.WEB.getName())) {
+			result = userClient.userInfo(tenantId, username, UserEnum.WEB.getName());
+		} else if (userType.equals(UserEnum.APP.getName())) {
+			result = userClient.userInfo(tenantId, username, UserEnum.APP.getName());
+		} else {
+			result = userClient.userInfo(tenantId, username, UserEnum.OTHER.getName());
+		}
+
+		// 判断返回信息
+		if (result.isSuccess()) {
+			UserInfo userInfo = result.getData();
+			User user = userInfo.getUser();
+			// 用户不存在,但提示用户名与密码错误并锁定账号
+			if (user == null || user.getId() == null) {
+				setFailCount(tenantId, username, count);
+				throw new UsernameNotFoundException(TokenUtil.USER_NOT_FOUND);
+			}
+			// 用户存在但密码错误,超过次数则锁定账号
+			if (grantType != null && !grantType.equals(TokenUtil.REFRESH_TOKEN_KEY) && !user.getPassword().equals(DigestUtil.hex(password))) {
+				setFailCount(tenantId, username, count);
+				throw new UsernameNotFoundException(TokenUtil.USER_NOT_FOUND);
+			}
+			// 用户角色不存在
+			if (Func.isEmpty(userInfo.getRoles())) {
+				throw new UserDeniedAuthorizationException(TokenUtil.USER_HAS_NO_ROLE);
+			}
+			// 多部门情况下指定单部门
+			if (Func.isNotEmpty(headerDept) && user.getDeptId().contains(headerDept)) {
+				user.setDeptId(headerDept);
+			}
+			// 多角色情况下指定单角色
+			if (Func.isNotEmpty(headerRole) && user.getRoleId().contains(headerRole)) {
+				R<List<String>> roleResult = sysClient.getRoleAliases(headerRole);
+				if (roleResult.isSuccess()) {
+					userInfo.setRoles(roleResult.getData());
+				}
+				user.setRoleId(headerRole);
+			}
+			// 成功则清除登录错误次数
+			delFailCount(tenantId, username);
+			return new BladeUserDetails(user.getId(),
+				user.getTenantId(), StringPool.EMPTY, user.getName(), user.getRealName(), user.getDeptId(), user.getPostId(), user.getRoleId(), Func.join(userInfo.getRoles()), Func.toStr(user.getAvatar(), TokenUtil.DEFAULT_AVATAR),
+				username, AuthConstant.ENCRYPT + user.getPassword(), userInfo.getDetail(), true, true, true, true,
+				AuthorityUtils.commaSeparatedStringToAuthorityList(Func.join(result.getData().getRoles())));
+		} else {
+			throw new UsernameNotFoundException(result.getMsg());
+		}
+	}
+
+	/**
+	 * 获取账号错误次数
+	 *
+	 * @param tenantId 租户id
+	 * @param username 账号
+	 * @return int
+	 */
+	private int getFailCount(String tenantId, String username) {
+		return Func.toInt(bladeRedis.get(CacheNames.tenantKey(tenantId, CacheNames.USER_FAIL_KEY, username)), 0);
+	}
+
+	/**
+	 * 设置账号错误次数
+	 *
+	 * @param tenantId 租户id
+	 * @param username 账号
+	 * @param count    次数
+	 */
+	private void setFailCount(String tenantId, String username, int count) {
+		bladeRedis.setEx(CacheNames.tenantKey(tenantId, CacheNames.USER_FAIL_KEY, username), count + 1, Duration.ofMinutes(30));
+	}
+
+	/**
+	 * 清空账号错误次数
+	 *
+	 * @param tenantId 租户id
+	 * @param username 账号
+	 */
+	private void delFailCount(String tenantId, String username) {
+		bladeRedis.del(CacheNames.tenantKey(tenantId, CacheNames.USER_FAIL_KEY, username));
+	}
+
+	/**
+	 * 校验refreshToken合法性
+	 *
+	 * @param grantType 认证类型
+	 * @param request   请求
+	 */
+	private boolean judgeRefreshToken(String grantType, HttpServletRequest request) {
+		if (jwtProperties.getState() && jwtProperties.getSingle() && StringUtil.equals(grantType, TokenUtil.REFRESH_TOKEN_KEY)) {
+			String refreshToken = request.getParameter(TokenUtil.REFRESH_TOKEN_KEY);
+			Claims claims = JwtUtil.parseJWT(refreshToken);
+			String tenantId = String.valueOf(claims.get("tenant_id"));
+			String userId = String.valueOf(claims.get("user_id"));
+			String token = JwtUtil.getRefreshToken(tenantId, userId, refreshToken);
+			return StringUtil.equalsIgnoreCase(token, refreshToken);
+		}
+		return true;
+	}
+
+
+}

+ 86 - 0
blade-auth/src/main/java/org/springblade/auth/support/BladeJwtTokenEnhancer.java

@@ -0,0 +1,86 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.auth.support;
+
+import lombok.AllArgsConstructor;
+import org.springblade.auth.service.BladeUserDetails;
+import org.springblade.auth.utils.TokenUtil;
+import org.springblade.core.jwt.JwtUtil;
+import org.springblade.core.jwt.props.JwtProperties;
+import org.springblade.core.tool.utils.Func;
+import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
+import org.springframework.security.oauth2.common.OAuth2AccessToken;
+import org.springframework.security.oauth2.common.OAuth2RefreshToken;
+import org.springframework.security.oauth2.provider.OAuth2Authentication;
+import org.springframework.security.oauth2.provider.token.TokenEnhancer;
+import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * jwt返回参数增强
+ *
+ * @author Chill
+ */
+@AllArgsConstructor
+public class BladeJwtTokenEnhancer implements TokenEnhancer {
+
+	private final JwtAccessTokenConverter jwtAccessTokenConverter;
+	private final JwtProperties jwtProperties;
+
+	@Override
+	public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
+		BladeUserDetails principal = (BladeUserDetails) authentication.getUserAuthentication().getPrincipal();
+
+		//token参数增强
+		Map<String, Object> info = new HashMap<>(16);
+		info.put(TokenUtil.CLIENT_ID, TokenUtil.getClientIdFromHeader());
+		info.put(TokenUtil.USER_ID, Func.toStr(principal.getUserId()));
+		info.put(TokenUtil.DEPT_ID, Func.toStr(principal.getDeptId()));
+		info.put(TokenUtil.POST_ID, Func.toStr(principal.getPostId()));
+		info.put(TokenUtil.ROLE_ID, Func.toStr(principal.getRoleId()));
+		info.put(TokenUtil.TENANT_ID, principal.getTenantId());
+		info.put(TokenUtil.OAUTH_ID, principal.getOauthId());
+		info.put(TokenUtil.ACCOUNT, principal.getAccount());
+		info.put(TokenUtil.USER_NAME, principal.getUsername());
+		info.put(TokenUtil.NICK_NAME, principal.getName());
+		info.put(TokenUtil.REAL_NAME, principal.getRealName());
+		info.put(TokenUtil.ROLE_NAME, principal.getRoleName());
+		info.put(TokenUtil.AVATAR, principal.getAvatar());
+		info.put(TokenUtil.DETAIL, principal.getDetail());
+		info.put(TokenUtil.LICENSE, TokenUtil.LICENSE_NAME);
+		((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(info);
+
+		//token状态设置
+		if (jwtProperties.getState()) {
+			OAuth2AccessToken oAuth2AccessToken = jwtAccessTokenConverter.enhance(accessToken, authentication);
+			String accessTokenValue = oAuth2AccessToken.getValue();
+			String tenantId = principal.getTenantId();
+			String userId = Func.toStr(principal.getUserId());
+			JwtUtil.addAccessToken(tenantId, userId, accessTokenValue, accessToken.getExpiresIn());
+
+			if (jwtProperties.getSingle()) {
+				OAuth2RefreshToken oAuth2RefreshToken = oAuth2AccessToken.getRefreshToken();
+				String refreshTokenValue = oAuth2RefreshToken.getValue();
+				JwtUtil.addRefreshToken(tenantId, userId, refreshTokenValue, accessToken.getExpiresIn() * 168);
+			}
+		}
+
+		return accessToken;
+	}
+}

+ 50 - 0
blade-auth/src/main/java/org/springblade/auth/support/BladeNoOpPasswordEncoder.java

@@ -0,0 +1,50 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.auth.support;
+
+import org.springframework.security.crypto.password.PasswordEncoder;
+
+/**
+ * 无密码加密
+ *
+ * @author Chill
+ */
+public class BladeNoOpPasswordEncoder implements PasswordEncoder {
+
+	@Override
+	public String encode(CharSequence rawPassword) {
+		return rawPassword.toString();
+	}
+
+	@Override
+	public boolean matches(CharSequence rawPassword, String encodedPassword) {
+		return rawPassword.toString().equals(encodedPassword);
+	}
+
+	/**
+	 * Get the singleton {@link BladeNoOpPasswordEncoder}.
+	 */
+	public static PasswordEncoder getInstance() {
+		return INSTANCE;
+	}
+
+	private static final PasswordEncoder INSTANCE = new BladeNoOpPasswordEncoder();
+
+	private BladeNoOpPasswordEncoder() {
+	}
+
+}

+ 39 - 0
blade-auth/src/main/java/org/springblade/auth/support/BladePasswordEncoder.java

@@ -0,0 +1,39 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.auth.support;
+
+import org.springblade.core.tool.utils.DigestUtil;
+import org.springframework.security.crypto.password.PasswordEncoder;
+
+/**
+ * 自定义密码加密
+ *
+ * @author Chill
+ */
+public class BladePasswordEncoder implements PasswordEncoder {
+
+	@Override
+	public String encode(CharSequence rawPassword) {
+		return DigestUtil.hex((String) rawPassword);
+	}
+
+	@Override
+	public boolean matches(CharSequence rawPassword, String encodedPassword) {
+		return encodedPassword.equals(encode(rawPassword));
+	}
+
+}

+ 66 - 0
blade-auth/src/main/java/org/springblade/auth/support/BladePasswordEncoderFactories.java

@@ -0,0 +1,66 @@
+/*
+ * Copyright 2002-2017 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springblade.auth.support;
+
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.DelegatingPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder;
+import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 自定义密码工厂
+ *
+ * @author Rob Winch, Chill
+ * @since 5.0
+ */
+public class BladePasswordEncoderFactories {
+
+	/**
+	 * Creates a {@link DelegatingPasswordEncoder} with default mappings. Additional
+	 * mappings may be added and the encoding will be updated to conform with best
+	 * practices. However, due to the nature of {@link DelegatingPasswordEncoder} the
+	 * updates should not impact users. The mappings current are:
+	 *
+	 * <ul>
+	 * <li>blade - {@link BladePasswordEncoder} (sha1(md5("password")))</li>
+	 * <li>bcrypt - {@link BCryptPasswordEncoder} (Also used for encoding)</li>
+	 * <li>noop - {@link BladeNoOpPasswordEncoder}</li>
+	 * <li>pbkdf2 - {@link Pbkdf2PasswordEncoder}</li>
+	 * <li>scrypt - {@link SCryptPasswordEncoder}</li>
+	 * </ul>
+	 *
+	 * @return the {@link PasswordEncoder} to use
+	 */
+	public static PasswordEncoder createDelegatingPasswordEncoder() {
+		String encodingId = "blade";
+		Map<String, PasswordEncoder> encoders = new HashMap<>(16);
+		encoders.put(encodingId, new BladePasswordEncoder());
+		encoders.put("bcrypt", new BCryptPasswordEncoder());
+		encoders.put("noop", BladeNoOpPasswordEncoder.getInstance());
+		encoders.put("pbkdf2", new Pbkdf2PasswordEncoder());
+		encoders.put("scrypt", new SCryptPasswordEncoder());
+
+		return new DelegatingPasswordEncoder(encodingId, encoders);
+	}
+
+	private BladePasswordEncoderFactories() {
+	}
+
+}

+ 182 - 0
blade-auth/src/main/java/org/springblade/auth/utils/TokenUtil.java

@@ -0,0 +1,182 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.auth.utils;
+
+import lombok.SneakyThrows;
+import org.springblade.common.constant.TenantConstant;
+import org.springblade.core.launch.constant.TokenConstant;
+import org.springblade.core.tenant.BladeTenantProperties;
+import org.springblade.core.tool.constant.BladeConstant;
+import org.springblade.core.tool.jackson.JsonUtil;
+import org.springblade.core.tool.utils.*;
+import org.springblade.system.entity.Tenant;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.oauth2.common.exceptions.UnapprovedClientAuthenticationException;
+import org.springframework.security.oauth2.common.exceptions.UserDeniedAuthorizationException;
+
+import java.util.Base64;
+import java.util.Calendar;
+import java.util.Date;
+
+/**
+ * 认证工具类
+ *
+ * @author Chill
+ */
+public class TokenUtil {
+
+	public final static String AVATAR = TokenConstant.AVATAR;
+	public final static String ACCOUNT = TokenConstant.ACCOUNT;
+	public final static String USER_NAME = TokenConstant.USER_NAME;
+	public final static String NICK_NAME = TokenConstant.NICK_NAME;
+	public final static String REAL_NAME = TokenConstant.REAL_NAME;
+	public final static String USER_ID = TokenConstant.USER_ID;
+	public final static String DEPT_ID = TokenConstant.DEPT_ID;
+	public final static String POST_ID = TokenConstant.POST_ID;
+	public final static String ROLE_ID = TokenConstant.ROLE_ID;
+	public final static String ROLE_NAME = TokenConstant.ROLE_NAME;
+	public final static String TENANT_ID = TokenConstant.TENANT_ID;
+	public final static String OAUTH_ID = TokenConstant.OAUTH_ID;
+	public final static String CLIENT_ID = TokenConstant.CLIENT_ID;
+	public final static String DETAIL = TokenConstant.DETAIL;
+	public final static String LICENSE = TokenConstant.LICENSE;
+	public final static String LICENSE_NAME = TokenConstant.LICENSE_NAME;
+
+	public final static String DEPT_HEADER_KEY = "Dept-Id";
+	public final static String ROLE_HEADER_KEY = "Role-Id";
+	public final static String CAPTCHA_HEADER_KEY = "Captcha-Key";
+	public final static String CAPTCHA_HEADER_CODE = "Captcha-Code";
+	public final static String CAPTCHA_NOT_CORRECT = "验证码不正确";
+	public final static String TENANT_HEADER_KEY = "Tenant-Id";
+	public final static String TENANT_PARAM_KEY = "tenant_id";
+	public final static String DEFAULT_TENANT_ID = "000000";
+	public final static String TENANT_NOT_FOUND = "租户ID未找到";
+	public final static String USER_TYPE_HEADER_KEY = "User-Type";
+	public final static String DEFAULT_USER_TYPE = "web";
+	public final static String TOKEN_NOT_PERMISSION = "令牌授权已过期";
+	public final static String USER_NOT_FOUND = "用户名或密码错误";
+	public final static String USER_HAS_NO_ROLE = "未获得用户的角色信息";
+	public final static String USER_HAS_NO_TENANT = "未获得用户的租户信息";
+	public final static String USER_HAS_NO_TENANT_PERMISSION = "租户授权已过期,请联系管理员";
+	public final static String USER_HAS_TOO_MANY_FAILS = "登录错误次数过多,请稍后再试";
+	public final static String HEADER_KEY = "Authorization";
+	public final static String HEADER_PREFIX = "Basic ";
+	public final static String DEFAULT_AVATAR = "";
+	public final static String PASSWORD_KEY = "password";
+	public final static String GRANT_TYPE_KEY = "grant_type";
+	public final static String REFRESH_TOKEN_KEY = "refresh_token";
+
+	private static BladeTenantProperties tenantProperties;
+
+	/**
+	 * 获取租户配置
+	 *
+	 * @return tenantProperties
+	 */
+	private static BladeTenantProperties getTenantProperties() {
+		if (tenantProperties == null) {
+			tenantProperties = SpringUtil.getBean(BladeTenantProperties.class);
+		}
+		return tenantProperties;
+	}
+
+	/**
+	 * 解码
+	 */
+	@SneakyThrows
+	public static String[] extractAndDecodeHeader() {
+		String header = WebUtil.getRequest().getHeader(TokenUtil.HEADER_KEY);
+		if (header == null || !header.startsWith(TokenUtil.HEADER_PREFIX)) {
+			throw new UnapprovedClientAuthenticationException("请求头中无client信息");
+		}
+
+		byte[] base64Token = header.substring(6).getBytes(Charsets.UTF_8_NAME);
+
+		byte[] decoded;
+		try {
+			decoded = Base64.getDecoder().decode(base64Token);
+		} catch (IllegalArgumentException var7) {
+			throw new BadCredentialsException("Failed to decode basic authentication token");
+		}
+
+		String token = new String(decoded, Charsets.UTF_8_NAME);
+		int index = token.indexOf(StringPool.COLON);
+		if (index == -1) {
+			throw new BadCredentialsException("Invalid basic authentication token");
+		} else {
+			return new String[]{token.substring(0, index), token.substring(index + 1)};
+		}
+	}
+
+	/**
+	 * 获取请求头中的客户端id
+	 */
+	public static String getClientIdFromHeader() {
+		String[] tokens = extractAndDecodeHeader();
+		return tokens[0];
+	}
+
+	/**
+	 * 获取token过期时间(次日凌晨3点)
+	 *
+	 * @return expire
+	 */
+	public static int getTokenValiditySecond() {
+		Calendar cal = Calendar.getInstance();
+		cal.add(Calendar.DAY_OF_YEAR, 1);
+		cal.set(Calendar.HOUR_OF_DAY, 3);
+		cal.set(Calendar.SECOND, 0);
+		cal.set(Calendar.MINUTE, 0);
+		cal.set(Calendar.MILLISECOND, 0);
+		return (int) (cal.getTimeInMillis() - System.currentTimeMillis()) / 1000;
+	}
+
+	/**
+	 * 获取refreshToken过期时间
+	 *
+	 * @return expire
+	 */
+	public static int getRefreshTokenValiditySeconds() {
+		return 60 * 60 * 24 * 15;
+	}
+
+	/**
+	 * 判断租户权限
+	 *
+	 * @param tenant 租户信息
+	 * @return boolean
+	 */
+	public static boolean judgeTenant(Tenant tenant) {
+		if (tenant == null || tenant.getId() == null) {
+			throw new UserDeniedAuthorizationException(TokenUtil.USER_HAS_NO_TENANT);
+		}
+		if (StringUtil.equalsIgnoreCase(tenant.getTenantId(), BladeConstant.ADMIN_TENANT_ID)) {
+			return false;
+		}
+		Date expireTime = tenant.getExpireTime();
+		if (getTenantProperties().getLicense()) {
+			String licenseKey = tenant.getLicenseKey();
+			String decrypt = DesUtil.decryptFormHex(licenseKey, TenantConstant.DES_KEY);
+			expireTime = JsonUtil.parse(decrypt, Tenant.class).getExpireTime();
+		}
+		if (expireTime != null && expireTime.before(DateUtil.now())) {
+			throw new UserDeniedAuthorizationException(TokenUtil.USER_HAS_NO_TENANT_PERMISSION);
+		}
+		return false;
+	}
+
+}

+ 15 - 0
blade-auth/src/main/resources/application-dev.yml

@@ -0,0 +1,15 @@
+#服务器端口
+server:
+  port: 8100
+
+#数据源配置
+spring:
+  datasource:
+    url: ${blade.datasource.dev.url}
+    username: ${blade.datasource.dev.username}
+    password: ${blade.datasource.dev.password}
+
+#第三方登陆
+social:
+  enabled: true
+  domain: http://127.0.0.1:1888

+ 15 - 0
blade-auth/src/main/resources/application-prod.yml

@@ -0,0 +1,15 @@
+#服务器端口
+server:
+  port: 8100
+
+#数据源配置
+spring:
+  datasource:
+    url: ${blade.datasource.prod.url}
+    username: ${blade.datasource.prod.username}
+    password: ${blade.datasource.prod.password}
+
+#第三方登陆
+social:
+  enabled: true
+  domain: http://127.0.0.1:1888

+ 15 - 0
blade-auth/src/main/resources/application-test.yml

@@ -0,0 +1,15 @@
+#服务器端口
+server:
+  port: 8100
+
+#数据源配置
+spring:
+  datasource:
+    url: ${blade.datasource.test.url}
+    username: ${blade.datasource.test.username}
+    password: ${blade.datasource.test.password}
+
+#第三方登陆
+social:
+  enabled: true
+  domain: http://127.0.0.1:1888

+ 50 - 0
blade-auth/src/main/resources/application.yml

@@ -0,0 +1,50 @@
+# 在使用Spring默认数据源Hikari的情况下配置以下配置项
+spring:
+  datasource:
+    hikari:
+      # 自动提交从池中返回的连接
+      auto-commit: true
+      # 连接池中维护的最小空闲连接数
+      minimum-idle: 10
+      # 连接池中允许的最大连接数。缺省值:10;推荐的公式:((core_count * 2) + effective_spindle_count)
+      maximum-pool-size: 60
+      # 空闲连接超时时间,默认值600000(10分钟),大于等于max-lifetime且max-lifetime>0,会被重置为0;不等于0且小于10秒,会被重置为10秒。
+      # 只有空闲连接数大于最大连接数且空闲时间超过该值,才会被释放
+      idle-timeout: 30000
+      # 连接最大存活时间.不等于0且小于30秒,会被重置为默认值30分钟.设置应该比mysql设置的超时时间短
+      max-lifetime: 1800000
+      # 等待连接池分配连接的最大时长(毫秒),超过这个时长还没可用的连接则发生SQLException, 缺省:30秒
+      connection-timeout: 30000
+      # 连接测试查询
+      connection-test-query: select 1
+      #connection-test-query: select 1 from dual
+
+#swagger文档
+swagger:
+  base-packages:
+    - org.springblade
+    - org.springframework.security.oauth2.provider.endpoint
+
+#第三方登陆
+social:
+  oauth:
+    GITHUB:
+      client-id: 233************
+      client-secret: 233************************************
+      redirect-uri: ${social.domain}/oauth/redirect/github
+    GITEE:
+      client-id: 233************
+      client-secret: 233************************************
+      redirect-uri: ${social.domain}/oauth/redirect/gitee
+    WECHAT_OPEN:
+      client-id: 233************
+      client-secret: 233************************************
+      redirect-uri: ${social.domain}/oauth/redirect/wechat
+    QQ:
+      client-id: 233************
+      client-secret: 233************************************
+      redirect-uri: ${social.domain}/oauth/redirect/qq
+    DINGTALK:
+      client-id: 233************
+      client-secret: 233************************************
+      redirect-uri: ${social.domain}/oauth/redirect/dingtalk

+ 127 - 0
blade-common/blade-common.iml

@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
+  <component name="FacetManager">
+    <facet type="Spring" name="Spring">
+      <configuration />
+    </facet>
+    <facet type="web" name="Web">
+      <configuration>
+        <webroots />
+      </configuration>
+    </facet>
+  </component>
+  <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
+    <output url="file://$MODULE_DIR$/target/classes" />
+    <output-test url="file://$MODULE_DIR$/target/test-classes" />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
+      <excludeFolder url="file://$MODULE_DIR$/target" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-core-launch:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-web:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-autoconfigure:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-logging:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: ch.qos.logback:logback-classic:1.2.11" level="project" />
+    <orderEntry type="library" name="Maven: ch.qos.logback:logback-core:1.2.11" level="project" />
+    <orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-to-slf4j:2.17.2" level="project" />
+    <orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-api:2.17.2" level="project" />
+    <orderEntry type="library" name="Maven: org.slf4j:jul-to-slf4j:1.7.30" level="project" />
+    <orderEntry type="library" name="Maven: jakarta.annotation:jakarta.annotation-api:1.3.5" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-json:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.module:jackson-module-parameter-names:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-web:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-beans:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-webmvc:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-context:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-expression:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-undertow:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: io.undertow:undertow-core:2.1.7.Final" level="project" />
+    <orderEntry type="library" name="Maven: org.jboss.logging:jboss-logging:3.4.2.Final" level="project" />
+    <orderEntry type="library" name="Maven: org.jboss.xnio:xnio-api:3.8.0.Final" level="project" />
+    <orderEntry type="library" name="Maven: org.wildfly.common:wildfly-common:1.5.2.Final" level="project" />
+    <orderEntry type="library" name="Maven: org.wildfly.client:wildfly-client-config:1.0.1.Final" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: org.jboss.xnio:xnio-nio:3.8.0.Final" level="project" />
+    <orderEntry type="library" name="Maven: org.jboss.threads:jboss-threads:3.1.0.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.undertow:undertow-servlet:2.1.7.Final" level="project" />
+    <orderEntry type="library" name="Maven: org.jboss.spec.javax.annotation:jboss-annotations-api_1.3_spec:2.0.1.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.undertow:undertow-websockets-jsr:2.1.7.Final" level="project" />
+    <orderEntry type="library" name="Maven: org.jboss.spec.javax.websocket:jboss-websocket-api_1.1_spec:2.0.0.Final" level="project" />
+    <orderEntry type="library" name="Maven: jakarta.servlet:jakarta.servlet-api:4.0.4" level="project" />
+    <orderEntry type="library" name="Maven: org.glassfish:jakarta.el:3.0.3" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-aop:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-aop:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.aspectj:aspectjweaver:1.9.6" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.retry:spring-retry:1.2.5.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-core:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-jcl:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-starter-ribbon:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-starter-netflix-ribbon:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-starter:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.security:spring-security-rsa:1.0.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.bouncycastle:bcpkix-jdk15on:1.59" level="project" />
+    <orderEntry type="library" name="Maven: org.bouncycastle:bcprov-jdk15on:1.59" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-netflix-ribbon:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-netflix-archaius:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-starter-netflix-archaius:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.netflix.archaius:archaius-core:0.7.7" level="project" />
+    <orderEntry type="library" name="Maven: com.google.code.findbugs:jsr305:3.0.2" level="project" />
+    <orderEntry type="library" name="Maven: commons-configuration:commons-configuration:1.8" level="project" />
+    <orderEntry type="library" name="Maven: com.netflix.ribbon:ribbon:2.3.0" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: com.netflix.ribbon:ribbon-transport:2.3.0" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: io.reactivex:rxnetty-contexts:0.4.9" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: io.reactivex:rxnetty-servo:0.4.9" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: com.netflix.hystrix:hystrix-core:1.5.18" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: org.hdrhistogram:HdrHistogram:2.1.9" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: javax.inject:javax.inject:1" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: io.reactivex:rxnetty:0.4.9" level="project" />
+    <orderEntry type="library" name="Maven: com.google.guava:guava:30.1.1-jre" level="project" />
+    <orderEntry type="library" name="Maven: com.google.guava:failureaccess:1.0.1" level="project" />
+    <orderEntry type="library" name="Maven: com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava" level="project" />
+    <orderEntry type="library" name="Maven: org.checkerframework:checker-qual:3.8.0" level="project" />
+    <orderEntry type="library" name="Maven: com.google.errorprone:error_prone_annotations:2.5.1" level="project" />
+    <orderEntry type="library" name="Maven: com.google.j2objc:j2objc-annotations:1.3" level="project" />
+    <orderEntry type="library" name="Maven: com.netflix.ribbon:ribbon-core:2.3.0" level="project" />
+    <orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.7.30" level="project" />
+    <orderEntry type="library" name="Maven: commons-lang:commons-lang:2.6" level="project" />
+    <orderEntry type="library" name="Maven: com.netflix.ribbon:ribbon-httpclient:2.3.0" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: commons-collections:commons-collections:3.2.2" level="project" />
+    <orderEntry type="library" name="Maven: org.apache.httpcomponents:httpclient:4.5.13" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: com.sun.jersey:jersey-client:1.19.1" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: com.sun.jersey:jersey-core:1.19.1" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: javax.ws.rs:jsr311-api:1.1.1" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: com.sun.jersey.contribs:jersey-apache-client4:1.19.1" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: com.netflix.servo:servo-core:0.12.21" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: com.netflix.netflix-commons:netflix-commons-util:0.3.0" level="project" />
+    <orderEntry type="library" name="Maven: com.netflix.ribbon:ribbon-loadbalancer:2.3.0" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: com.netflix.netflix-commons:netflix-statistics:0.1.1" level="project" />
+    <orderEntry type="library" name="Maven: io.reactivex:rxjava:1.3.8" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-discovery:2.2.7.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.cloud:spring-cloud-alibaba-commons:2.2.7.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.spring:spring-context-support:1.0.10" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-commons:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.security:spring-security-crypto:5.3.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-context:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-config:2.2.7.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.nacos:nacos-client:2.0.4" level="project" />
+    <orderEntry type="library" name="Maven: commons-codec:commons-codec:1.14" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-core:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-databind:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: org.apache.httpcomponents:httpasyncclient:4.1.4" level="project" />
+    <orderEntry type="library" name="Maven: org.apache.httpcomponents:httpcore:4.4.14" level="project" />
+    <orderEntry type="library" name="Maven: org.apache.httpcomponents:httpcore-nio:4.4.14" level="project" />
+    <orderEntry type="library" name="Maven: org.reflections:reflections:0.9.11" level="project" />
+    <orderEntry type="library" name="Maven: org.javassist:javassist:3.21.0-GA" level="project" />
+    <orderEntry type="library" name="Maven: io.prometheus:simpleclient:0.5.0" level="project" />
+    <orderEntry type="library" name="Maven: org.yaml:snakeyaml:1.26" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.springblade:blade-core-auto:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.projectlombok:lombok:1.18.22" level="project" />
+  </component>
+</module>

+ 46 - 0
blade-common/pom.xml

@@ -0,0 +1,46 @@
+<?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>BladeX</artifactId>
+        <groupId>org.springblade</groupId>
+        <version>2.9.1.RELEASE</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>blade-common</artifactId>
+    <name>${project.artifactId}</name>
+    <version>${bladex.project.version}</version>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-core-launch</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-starter-ribbon</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-core-auto</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <configuration>
+                    <skip>true</skip>
+                    <finalName>${project.name}</finalName>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 59 - 0
blade-common/src/main/java/org/springblade/common/cache/CacheNames.java

@@ -0,0 +1,59 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.common.cache;
+
+/**
+ * 缓存名
+ *
+ * @author Chill
+ */
+public interface CacheNames {
+
+	/**
+	 * 返回拼接后的key
+	 *
+	 * @param cacheKey      缓存key
+	 * @param cacheKeyValue 缓存key值
+	 * @return tenantKey
+	 */
+	static String cacheKey(String cacheKey, String cacheKeyValue) {
+		return cacheKey.concat(cacheKeyValue);
+	}
+
+	/**
+	 * 返回租户格式的key
+	 *
+	 * @param tenantId      租户编号
+	 * @param cacheKey      缓存key
+	 * @param cacheKeyValue 缓存key值
+	 * @return tenantKey
+	 */
+	static String tenantKey(String tenantId, String cacheKey, String cacheKeyValue) {
+		return tenantId.concat(":").concat(cacheKey).concat(cacheKeyValue);
+	}
+
+	/**
+	 * 验证码key
+	 */
+	String CAPTCHA_KEY = "blade:auth::blade:captcha:";
+
+	/**
+	 * 登录失败key
+	 */
+	String USER_FAIL_KEY = "blade:user::blade:fail:";
+
+}

+ 32 - 0
blade-common/src/main/java/org/springblade/common/config/BladeCommonConfiguration.java

@@ -0,0 +1,32 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.common.config;
+
+
+import lombok.AllArgsConstructor;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * 公共封装包配置类
+ *
+ * @author Chill
+ */
+@Configuration
+@AllArgsConstructor
+public class BladeCommonConfiguration {
+
+}

+ 77 - 0
blade-common/src/main/java/org/springblade/common/constant/CommonConstant.java

@@ -0,0 +1,77 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.common.constant;
+
+/**
+ * 通用常量
+ *
+ * @author Chill
+ */
+public interface CommonConstant {
+
+	/**
+	 * sword 系统名
+	 */
+	String SWORD_NAME = "sword";
+
+	/**
+	 * saber 系统名
+	 */
+	String SABER_NAME = "saber";
+
+	/**
+	 * 顶级父节点id
+	 */
+	Long TOP_PARENT_ID = 0L;
+
+	/**
+	 * 顶级父节点名称
+	 */
+	String TOP_PARENT_NAME = "顶级";
+
+	/**
+	 * 未封存状态值
+	 */
+	Integer NOT_SEALED_ID = 0;
+
+	/**
+	 * 默认密码
+	 */
+	String DEFAULT_PASSWORD = "123456";
+
+	/**
+	 * 默认密码参数值
+	 */
+	String DEFAULT_PARAM_PASSWORD = "account.initPassword";
+
+	/**
+	 * 默认排序字段
+	 */
+	String SORT_FIELD = "sort";
+
+	/**
+	 * 数据权限类型
+	 */
+	Integer DATA_SCOPE_CATEGORY = 1;
+
+	/**
+	 * 接口权限类型
+	 */
+	Integer API_SCOPE_CATEGORY = 2;
+
+
+}

+ 230 - 0
blade-common/src/main/java/org/springblade/common/constant/LauncherConstant.java

@@ -0,0 +1,230 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.common.constant;
+
+import org.springblade.core.launch.constant.AppConstant;
+
+import static org.springblade.core.launch.constant.AppConstant.APPLICATION_NAME_PREFIX;
+
+/**
+ * 启动常量
+ *
+ * @author Chill
+ */
+public interface LauncherConstant {
+
+	/**
+	 * xxljob
+	 */
+	String APPLICATION_XXLJOB_NAME = APPLICATION_NAME_PREFIX + "xxljob";
+
+	/**
+	 * xxljob
+	 */
+	String APPLICATION_XXLJOB_ADMIN_NAME = APPLICATION_NAME_PREFIX + "xxljob-admin";
+
+	/**
+	 * nacos dev 地址
+	 */
+	String NACOS_DEV_ADDR = "127.0.0.1:8848";
+
+	/**
+	 * nacos prod 地址
+	 */
+	String NACOS_PROD_ADDR = "172.30.0.48:8848";
+
+	/**
+	 * nacos test 地址
+	 */
+	String NACOS_TEST_ADDR = "172.30.0.48:8848";
+
+	/**
+	 * sentinel dev 地址
+	 */
+	String SENTINEL_DEV_ADDR = "127.0.0.1:8858";
+
+	/**
+	 * sentinel prod 地址
+	 */
+	String SENTINEL_PROD_ADDR = "172.30.0.58:8858";
+
+	/**
+	 * sentinel test 地址
+	 */
+	String SENTINEL_TEST_ADDR = "172.30.0.58:8858";
+
+	/**
+	 * seata dev 地址
+	 */
+	String SEATA_DEV_ADDR = "127.0.0.1:8091";
+
+	/**
+	 * seata prod 地址
+	 */
+	String SEATA_PROD_ADDR = "172.30.0.68:8091";
+
+	/**
+	 * seata test 地址
+	 */
+	String SEATA_TEST_ADDR = "172.30.0.68:8091";
+
+	/**
+	 * zipkin dev 地址
+	 */
+	String ZIPKIN_DEV_ADDR = "http://127.0.0.1:9411";
+
+	/**
+	 * zipkin prod 地址
+	 */
+	String ZIPKIN_PROD_ADDR = "http://172.30.0.71:9411";
+
+	/**
+	 * zipkin test 地址
+	 */
+	String ZIPKIN_TEST_ADDR = "http://172.30.0.71:9411";
+
+	/**
+	 * elk dev 地址
+	 */
+	String ELK_DEV_ADDR = "127.0.0.1:9000";
+
+	/**
+	 * elk prod 地址
+	 */
+	String ELK_PROD_ADDR = "172.30.0.72:9000";
+
+	/**
+	 * elk test 地址
+	 */
+	String ELK_TEST_ADDR = "172.30.0.72:9000";
+
+	/**
+	 * seata file模式
+	 */
+	String FILE_MODE = "file";
+
+	/**
+	 * seata nacos模式
+	 */
+	String NACOS_MODE = "nacos";
+
+	/**
+	 * seata default模式
+	 */
+	String DEFAULT_MODE = "default";
+
+	/**
+	 * seata group后缀
+	 */
+	String GROUP_NAME = "-group";
+
+	/**
+	 * seata 服务组格式
+	 *
+	 * @param appName 服务名
+	 * @return group
+	 */
+	static String seataServiceGroup(String appName) {
+		return appName.concat(GROUP_NAME);
+	}
+
+	/**
+	 * 动态获取nacos地址
+	 *
+	 * @param profile 环境变量
+	 * @return addr
+	 */
+	static String nacosAddr(String profile) {
+		switch (profile) {
+			case (AppConstant.PROD_CODE):
+				return NACOS_PROD_ADDR;
+			case (AppConstant.TEST_CODE):
+				return NACOS_TEST_ADDR;
+			default:
+				return NACOS_DEV_ADDR;
+		}
+	}
+
+	/**
+	 * 动态获取sentinel地址
+	 *
+	 * @param profile 环境变量
+	 * @return addr
+	 */
+	static String sentinelAddr(String profile) {
+		switch (profile) {
+			case (AppConstant.PROD_CODE):
+				return SENTINEL_PROD_ADDR;
+			case (AppConstant.TEST_CODE):
+				return SENTINEL_TEST_ADDR;
+			default:
+				return SENTINEL_DEV_ADDR;
+		}
+	}
+
+	/**
+	 * 动态获取seata地址
+	 *
+	 * @param profile 环境变量
+	 * @return addr
+	 */
+	static String seataAddr(String profile) {
+		switch (profile) {
+			case (AppConstant.PROD_CODE):
+				return SEATA_PROD_ADDR;
+			case (AppConstant.TEST_CODE):
+				return SEATA_TEST_ADDR;
+			default:
+				return SEATA_DEV_ADDR;
+		}
+	}
+
+	/**
+	 * 动态获取zipkin地址
+	 *
+	 * @param profile 环境变量
+	 * @return addr
+	 */
+	static String zipkinAddr(String profile) {
+		switch (profile) {
+			case (AppConstant.PROD_CODE):
+				return ZIPKIN_PROD_ADDR;
+			case (AppConstant.TEST_CODE):
+				return ZIPKIN_TEST_ADDR;
+			default:
+				return ZIPKIN_DEV_ADDR;
+		}
+	}
+
+	/**
+	 * 动态获取elk地址
+	 *
+	 * @param profile 环境变量
+	 * @return addr
+	 */
+	static String elkAddr(String profile) {
+		switch (profile) {
+			case (AppConstant.PROD_CODE):
+				return ELK_PROD_ADDR;
+			case (AppConstant.TEST_CODE):
+				return ELK_TEST_ADDR;
+			default:
+				return ELK_DEV_ADDR;
+		}
+	}
+
+}

+ 66 - 0
blade-common/src/main/java/org/springblade/common/constant/TenantConstant.java

@@ -0,0 +1,66 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.common.constant;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * 租户常量
+ *
+ * @author Chill
+ */
+public interface TenantConstant {
+
+	/**
+	 * 租户默认密码KEY
+	 */
+	String PASSWORD_KEY = "tenant.default.password";
+
+	/**
+	 * 租户默认账号额度KEY
+	 */
+	String ACCOUNT_NUMBER_KEY = "tenant.default.accountNumber";
+
+	/**
+	 * 租户默认菜单集合KEY
+	 */
+	String ACCOUNT_MENU_CODE_KEY = "tenant.default.menuCode";
+
+	/**
+	 * 租户默认密码
+	 */
+	String DEFAULT_PASSWORD = "123456";
+
+	/**
+	 * 租户授权码默认16位密钥
+	 */
+	String DES_KEY = "0000000000000000";
+
+	/**
+	 * 租户默认账号额度
+	 */
+	Integer DEFAULT_ACCOUNT_NUMBER = -1;
+
+	/**
+	 * 租户默认菜单集合
+	 */
+	List<String> MENU_CODES = Arrays.asList(
+		"desk", "flow", "work", "monitor", "resource", "role", "user", "dept", "dictbiz", "topmenu"
+	);
+
+}

+ 61 - 0
blade-common/src/main/java/org/springblade/common/launch/LauncherServiceImpl.java

@@ -0,0 +1,61 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.common.launch;
+
+import org.springblade.common.constant.LauncherConstant;
+import org.springblade.core.auto.service.AutoService;
+import org.springblade.core.launch.service.LauncherService;
+import org.springblade.core.launch.utils.PropsUtil;
+import org.springframework.boot.builder.SpringApplicationBuilder;
+
+import java.util.Properties;
+
+/**
+ * 启动参数拓展
+ *
+ * @author smallchil
+ */
+@AutoService(LauncherService.class)
+public class LauncherServiceImpl implements LauncherService {
+
+	@Override
+	public void launcher(SpringApplicationBuilder builder, String appName, String profile, boolean isLocalDev) {
+		Properties props = System.getProperties();
+		// 通用注册
+		PropsUtil.setProperty(props, "spring.cloud.nacos.discovery.server-addr", LauncherConstant.nacosAddr(profile));
+		PropsUtil.setProperty(props, "spring.cloud.nacos.config.server-addr", LauncherConstant.nacosAddr(profile));
+		PropsUtil.setProperty(props, "spring.cloud.sentinel.transport.dashboard", LauncherConstant.sentinelAddr(profile));
+		PropsUtil.setProperty(props, "spring.zipkin.base-url", LauncherConstant.zipkinAddr(profile));
+		PropsUtil.setProperty(props, "spring.datasource.dynamic.enabled", "false");
+
+		// 开启elk日志
+		// PropsUtil.setProperty(props, "blade.log.elk.destination", LauncherConstant.elkAddr(profile));
+
+		// seata注册地址
+		// PropsUtil.setProperty(props, "seata.service.grouplist.default", LauncherConstant.seataAddr(profile));
+		// seata注册group格式
+		// PropsUtil.setProperty(props, "seata.tx-service-group", LauncherConstant.seataServiceGroup(appName));
+		// seata配置服务group
+		// PropsUtil.setProperty(props, "seata.service.vgroup-mapping.".concat(LauncherConstant.seataServiceGroup(appName)), LauncherConstant.DEFAULT_MODE);
+		// seata注册模式配置
+		// PropsUtil.setProperty(props, "seata.registry.type", LauncherConstant.NACOS_MODE);
+		// PropsUtil.setProperty(props, "seata.registry.nacos.server-addr", LauncherConstant.nacosAddr(profile));
+		// PropsUtil.setProperty(props, "seata.config.type", LauncherConstant.NACOS_MODE);
+		// PropsUtil.setProperty(props, "seata.config.nacos.server-addr", LauncherConstant.nacosAddr(profile));
+	}
+
+}

+ 26 - 0
blade-common/src/main/java/org/springblade/common/utils/CommonUtil.java

@@ -0,0 +1,26 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.common.utils;
+
+/**
+ * 通用工具类
+ *
+ * @author Chill
+ */
+public class CommonUtil {
+
+}

+ 8 - 0
blade-common/src/main/resources/banner.txt

@@ -0,0 +1,8 @@
+${AnsiColor.BLUE}                   ______  _             _       ___   ___
+${AnsiColor.BLUE}                   | ___ \| |           | |      \  \ /  /
+${AnsiColor.BLUE}                   | |_/ /| |  __ _   __| |  ___  \  V  /
+${AnsiColor.BLUE}                   | ___ \| | / _` | / _` | / _ \   > <
+${AnsiColor.BLUE}                   | |_/ /| || (_| || (_| ||  __/ /  .  \
+${AnsiColor.BLUE}                   \____/ |_| \__,_| \__,_| \___|/__/ \__\
+
+${AnsiColor.BLUE}:: BladeX ${blade.service.version} :: ${spring.application.name}:${AnsiColor.RED}${blade.env}${AnsiColor.BLUE} :: Running SpringBoot ${spring-boot.version} :: ${AnsiColor.BRIGHT_BLACK}

+ 15 - 0
blade-gateway/Dockerfile

@@ -0,0 +1,15 @@
+FROM bladex/alpine-java:openjdk8-openj9_cn_slim
+
+MAINTAINER bladejava@qq.com
+
+RUN mkdir -p /blade/gateway
+
+WORKDIR /blade/gateway
+
+EXPOSE 80
+
+ADD ./target/blade-gateway.jar ./app.jar
+
+ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "app.jar"]
+
+CMD ["--spring.profiles.active=test"]

+ 171 - 0
blade-gateway/blade-gateway.iml

@@ -0,0 +1,171 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
+  <component name="FacetManager">
+    <facet type="Spring" name="Spring">
+      <configuration />
+    </facet>
+  </component>
+  <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
+    <output url="file://$MODULE_DIR$/target/classes" />
+    <output-test url="file://$MODULE_DIR$/target/test-classes" />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
+      <excludeFolder url="file://$MODULE_DIR$/target" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-core-launch:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-aop:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-context:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-expression:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-logging:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: ch.qos.logback:logback-classic:1.2.11" level="project" />
+    <orderEntry type="library" name="Maven: ch.qos.logback:logback-core:1.2.11" level="project" />
+    <orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-to-slf4j:2.17.2" level="project" />
+    <orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-api:2.17.2" level="project" />
+    <orderEntry type="library" name="Maven: org.slf4j:jul-to-slf4j:1.7.30" level="project" />
+    <orderEntry type="library" name="Maven: jakarta.annotation:jakarta.annotation-api:1.3.5" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-aop:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-beans:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.aspectj:aspectjweaver:1.9.6" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.retry:spring-retry:1.2.5.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-core:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-jcl:5.2.15.RELEASE" level="project" />
+    <orderEntry type="module" module-name="blade-common" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-starter-ribbon:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-starter-metrics:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: io.micrometer:micrometer-core:1.6.3" level="project" />
+    <orderEntry type="library" name="Maven: org.hdrhistogram:HdrHistogram:2.1.12" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: org.latencyutils:LatencyUtils:2.0.3" level="project" />
+    <orderEntry type="library" name="Maven: io.micrometer:micrometer-registry-prometheus:1.6.3" level="project" />
+    <orderEntry type="library" name="Maven: io.prometheus:simpleclient_common:0.9.0" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.csp:sentinel-core:1.8.3" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba:druid:1.2.8" level="project" />
+    <orderEntry type="library" name="Maven: javax.annotation:javax.annotation-api:1.3.2" level="project" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-starter-jwt:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-data-redis:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.data:spring-data-redis:2.3.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.data:spring-data-keyvalue:2.3.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.data:spring-data-commons:2.3.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-tx:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-oxm:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-context-support:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: io.lettuce:lettuce-core:5.3.7.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-common:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-handler:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-resolver:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-buffer:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-codec:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-transport:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.projectreactor:reactor-core:3.3.17.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: io.jsonwebtoken:jjwt-impl:0.11.2" level="project" />
+    <orderEntry type="library" name="Maven: io.jsonwebtoken:jjwt-api:0.11.2" level="project" />
+    <orderEntry type="library" name="Maven: io.jsonwebtoken:jjwt-jackson:0.11.2" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba:fastjson:1.2.80" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-starter-gateway:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-starter:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.security:spring-security-rsa:1.0.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.bouncycastle:bcpkix-jdk15on:1.59" level="project" />
+    <orderEntry type="library" name="Maven: org.bouncycastle:bcprov-jdk15on:1.59" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-gateway-server:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-validation:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.glassfish:jakarta.el:3.0.3" level="project" />
+    <orderEntry type="library" name="Maven: org.hibernate.validator:hibernate-validator:6.1.7.Final" level="project" />
+    <orderEntry type="library" name="Maven: jakarta.validation:jakarta.validation-api:2.0.2" level="project" />
+    <orderEntry type="library" name="Maven: org.jboss.logging:jboss-logging:3.4.2.Final" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml:classmate:1.5.1" level="project" />
+    <orderEntry type="library" name="Maven: io.projectreactor.addons:reactor-extra:3.3.6.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-webflux:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-json:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.module:jackson-module-parameter-names:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-reactor-netty:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: io.projectreactor.netty:reactor-netty:0.9.20.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-codec-http:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-codec-http2:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-handler-proxy:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-codec-socks:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-transport-native-epoll:linux-x86_64:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-transport-native-unix-common:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-web:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-webflux:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.synchronoss.cloud:nio-multipart-parser:1.1.0" level="project" />
+    <orderEntry type="library" name="Maven: org.synchronoss.cloud:nio-stream-storage:1.1.3" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-data-redis-reactive:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: de.codecentric:spring-boot-admin-starter-client:2.3.1" level="project" />
+    <orderEntry type="library" name="Maven: de.codecentric:spring-boot-admin-client:2.3.1" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-actuator:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-actuator-autoconfigure:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-actuator:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-starter-netflix-hystrix:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-netflix-hystrix:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-autoconfigure:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-netflix-ribbon:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-netflix-archaius:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-starter-netflix-archaius:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.netflix.archaius:archaius-core:0.7.7" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: com.google.code.findbugs:jsr305:3.0.2" level="project" />
+    <orderEntry type="library" name="Maven: commons-configuration:commons-configuration:1.8" level="project" />
+    <orderEntry type="library" name="Maven: commons-lang:commons-lang:2.6" level="project" />
+    <orderEntry type="library" name="Maven: com.netflix.hystrix:hystrix-core:1.5.18" level="project" />
+    <orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.7.30" level="project" />
+    <orderEntry type="library" name="Maven: io.reactivex:rxjava:1.3.8" level="project" />
+    <orderEntry type="library" name="Maven: com.netflix.hystrix:hystrix-serialization:1.5.18" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: com.fasterxml.jackson.module:jackson-module-afterburner:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: com.netflix.hystrix:hystrix-metrics-event-stream:1.5.18" level="project" />
+    <orderEntry type="library" name="Maven: com.netflix.hystrix:hystrix-javanica:1.5.18" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: org.apache.commons:commons-lang3:3.10" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: org.ow2.asm:asm:5.0.4" level="project" />
+    <orderEntry type="library" name="Maven: com.google.guava:guava:30.1.1-jre" level="project" />
+    <orderEntry type="library" name="Maven: com.google.guava:failureaccess:1.0.1" level="project" />
+    <orderEntry type="library" name="Maven: com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava" level="project" />
+    <orderEntry type="library" name="Maven: org.checkerframework:checker-qual:3.8.0" level="project" />
+    <orderEntry type="library" name="Maven: com.google.errorprone:error_prone_annotations:2.5.1" level="project" />
+    <orderEntry type="library" name="Maven: com.google.j2objc:j2objc-annotations:1.3" level="project" />
+    <orderEntry type="library" name="Maven: io.reactivex:rxjava-reactive-streams:1.2.1" level="project" />
+    <orderEntry type="library" name="Maven: org.reactivestreams:reactive-streams:1.0.3" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-discovery:2.2.7.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.cloud:spring-cloud-alibaba-commons:2.2.7.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.spring:spring-context-support:1.0.10" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-commons:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.security:spring-security-crypto:5.3.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-context:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-starter-netflix-ribbon:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.netflix.ribbon:ribbon:2.3.0" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: com.netflix.ribbon:ribbon-transport:2.3.0" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: io.reactivex:rxnetty-contexts:0.4.9" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: io.reactivex:rxnetty-servo:0.4.9" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: javax.inject:javax.inject:1" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: io.reactivex:rxnetty:0.4.9" level="project" />
+    <orderEntry type="library" name="Maven: com.netflix.ribbon:ribbon-core:2.3.0" level="project" />
+    <orderEntry type="library" name="Maven: com.netflix.ribbon:ribbon-httpclient:2.3.0" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: commons-collections:commons-collections:3.2.2" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: com.sun.jersey:jersey-client:1.19.1" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: com.sun.jersey:jersey-core:1.19.1" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: javax.ws.rs:jsr311-api:1.1.1" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: com.sun.jersey.contribs:jersey-apache-client4:1.19.1" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: com.netflix.servo:servo-core:0.12.21" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: com.netflix.netflix-commons:netflix-commons-util:0.3.0" level="project" />
+    <orderEntry type="library" name="Maven: com.netflix.ribbon:ribbon-loadbalancer:2.3.0" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: com.netflix.netflix-commons:netflix-statistics:0.1.1" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-config:2.2.7.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.nacos:nacos-client:2.0.4" level="project" />
+    <orderEntry type="library" name="Maven: commons-codec:commons-codec:1.14" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-core:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-databind:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: org.apache.httpcomponents:httpasyncclient:4.1.4" level="project" />
+    <orderEntry type="library" name="Maven: org.apache.httpcomponents:httpcore:4.4.14" level="project" />
+    <orderEntry type="library" name="Maven: org.apache.httpcomponents:httpcore-nio:4.4.14" level="project" />
+    <orderEntry type="library" name="Maven: org.apache.httpcomponents:httpclient:4.5.13" level="project" />
+    <orderEntry type="library" name="Maven: org.reflections:reflections:0.9.11" level="project" />
+    <orderEntry type="library" name="Maven: org.javassist:javassist:3.21.0-GA" level="project" />
+    <orderEntry type="library" name="Maven: io.prometheus:simpleclient:0.5.0" level="project" />
+    <orderEntry type="library" name="Maven: org.yaml:snakeyaml:1.26" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.projectlombok:lombok:1.18.22" level="project" />
+  </component>
+</module>

+ 130 - 0
blade-gateway/pom.xml

@@ -0,0 +1,130 @@
+<?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>BladeX</artifactId>
+        <groupId>org.springblade</groupId>
+        <version>2.9.1.RELEASE</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>blade-gateway</artifactId>
+    <name>${project.artifactId}</name>
+    <version>${bladex.project.version}</version>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <!--Blade-->
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-core-launch</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-starter-web</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-starter-undertow</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-common</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.springblade</groupId>
+                    <artifactId>blade-core-launch</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-starter-metrics</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-starter-jwt</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+        </dependency>
+        <!--Spring-->
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-gateway</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>de.codecentric</groupId>
+            <artifactId>spring-boot-admin-starter-client</artifactId>
+        </dependency>
+        <!--Hystrix-->
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>commons-logging</groupId>
+                    <artifactId>commons-logging</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <!-- Nacos -->
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>com.alibaba.nacos</groupId>
+                    <artifactId>nacos-client</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>com.alibaba.nacos</groupId>
+                    <artifactId>nacos-client</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba.nacos</groupId>
+            <artifactId>nacos-client</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>com.spotify</groupId>
+                <artifactId>dockerfile-maven-plugin</artifactId>
+                <configuration>
+                    <username>${docker.username}</username>
+                    <password>${docker.password}</password>
+                    <repository>${docker.registry.url}/${docker.namespace}/${project.artifactId}</repository>
+                    <tag>${project.version}</tag>
+                    <useMavenSettingsForAuth>true</useMavenSettingsForAuth>
+                    <buildArgs>
+                        <JAR_FILE>target/${project.build.finalName}.jar</JAR_FILE>
+                    </buildArgs>
+                    <skip>false</skip>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-antrun-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 39 - 0
blade-gateway/src/main/java/org/springblade/gateway/GateWayApplication.java

@@ -0,0 +1,39 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.gateway;
+
+import org.springblade.core.launch.constant.AppConstant;
+import org.springblade.core.launch.BladeApplication;
+import org.springframework.cloud.client.SpringCloudApplication;
+import org.springframework.cloud.netflix.hystrix.EnableHystrix;
+import org.springframework.scheduling.annotation.EnableScheduling;
+
+/**
+ * 项目启动
+ *
+ * @author Chill
+ */
+@EnableHystrix
+@EnableScheduling
+@SpringCloudApplication
+public class GateWayApplication {
+
+	public static void main(String[] args) {
+		BladeApplication.run(AppConstant.APPLICATION_GATEWAY_NAME, GateWayApplication.class, args);
+	}
+
+}

+ 45 - 0
blade-gateway/src/main/java/org/springblade/gateway/config/ErrorHandlerConfiguration.java

@@ -0,0 +1,45 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.gateway.config;
+
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.springblade.gateway.handler.ErrorExceptionHandler;
+import org.springframework.boot.autoconfigure.AutoConfigureBefore;
+import org.springframework.boot.autoconfigure.web.ResourceProperties;
+import org.springframework.boot.autoconfigure.web.ServerProperties;
+import org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * 异常处理配置类
+ *
+ * @author Chill
+ */
+@Configuration
+@AutoConfigureBefore(ErrorWebFluxAutoConfiguration.class)
+@EnableConfigurationProperties({ServerProperties.class, ResourceProperties.class})
+public class ErrorHandlerConfiguration {
+
+	@Bean
+	public ErrorExceptionHandler globalExceptionHandler(ObjectMapper objectMapper) {
+		return new ErrorExceptionHandler(objectMapper);
+	}
+
+}

+ 81 - 0
blade-gateway/src/main/java/org/springblade/gateway/config/RouterFunctionConfiguration.java

@@ -0,0 +1,81 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.gateway.config;
+
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springblade.gateway.props.AuthProperties;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.http.server.reactive.ServerHttpResponse;
+import org.springframework.web.cors.reactive.CorsUtils;
+import org.springframework.web.server.ServerWebExchange;
+import org.springframework.web.server.WebFilter;
+import org.springframework.web.server.WebFilterChain;
+import reactor.core.publisher.Mono;
+
+/**
+ * 路由配置信息
+ *
+ * @author Chill
+ */
+@Slf4j
+@Configuration
+@AllArgsConstructor
+@EnableConfigurationProperties({AuthProperties.class})
+public class RouterFunctionConfiguration {
+
+	/**
+	 * 这里为支持的请求头,如果有自定义的header字段请自己添加
+	 */
+	private static final String ALLOWED_HEADERS = "X-Requested-With, Tenant-Id, Blade-Auth, Content-Type, Authorization, credential, X-XSRF-TOKEN, token, username, client, knfie4j-gateway-request, knife4j-gateway-code, request-origion";
+	private static final String ALLOWED_METHODS = "GET,POST,PUT,DELETE,OPTIONS,HEAD";
+	private static final String ALLOWED_ORIGIN = "*";
+	private static final String ALLOWED_EXPOSE = "*";
+	private static final String MAX_AGE = "18000L";
+
+	/**
+	 * 跨域配置
+	 */
+	@Bean
+	public WebFilter corsFilter() {
+		return (ServerWebExchange ctx, WebFilterChain chain) -> {
+			ServerHttpRequest request = ctx.getRequest();
+			if (CorsUtils.isCorsRequest(request)) {
+				ServerHttpResponse response = ctx.getResponse();
+				HttpHeaders headers = response.getHeaders();
+				headers.add("Access-Control-Allow-Headers", ALLOWED_HEADERS);
+				headers.add("Access-Control-Allow-Methods", ALLOWED_METHODS);
+				headers.add("Access-Control-Allow-Origin", ALLOWED_ORIGIN);
+				headers.add("Access-Control-Expose-Headers", ALLOWED_EXPOSE);
+				headers.add("Access-Control-Max-Age", MAX_AGE);
+				headers.add("Access-Control-Allow-Credentials", "true");
+				if (request.getMethod() == HttpMethod.OPTIONS) {
+					response.setStatusCode(HttpStatus.OK);
+					return Mono.empty();
+				}
+			}
+			return chain.filter(ctx);
+		};
+	}
+
+}

+ 101 - 0
blade-gateway/src/main/java/org/springblade/gateway/dynamic/DynamicRouteService.java

@@ -0,0 +1,101 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.gateway.dynamic;
+
+import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
+import org.springframework.cloud.gateway.route.RouteDefinition;
+import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.context.ApplicationEventPublisherAware;
+import org.springframework.stereotype.Service;
+import reactor.core.publisher.Mono;
+
+import java.util.List;
+
+/**
+ * 动态路由业务类
+ *
+ * @author Chill
+ */
+@Service
+public class DynamicRouteService implements ApplicationEventPublisherAware {
+
+	private final RouteDefinitionWriter routeDefinitionWriter;
+
+	private ApplicationEventPublisher publisher;
+
+	public DynamicRouteService(RouteDefinitionWriter routeDefinitionWriter) {
+		this.routeDefinitionWriter = routeDefinitionWriter;
+	}
+
+	@Override
+	public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
+		this.publisher = applicationEventPublisher;
+	}
+
+	/**
+	 * 增加路由
+	 */
+	public String save(RouteDefinition definition) {
+		try {
+			routeDefinitionWriter.save(Mono.just(definition)).subscribe();
+			this.publisher.publishEvent(new RefreshRoutesEvent(this));
+			return "save success";
+		} catch (Exception e) {
+			e.printStackTrace();
+			return "save failure";
+		}
+	}
+
+	/**
+	 * 更新路由
+	 */
+	public String update(RouteDefinition definition) {
+		try {
+			this.routeDefinitionWriter.delete(Mono.just(definition.getId()));
+			this.routeDefinitionWriter.save(Mono.just(definition)).subscribe();
+			this.publisher.publishEvent(new RefreshRoutesEvent(this));
+			return "update success";
+		} catch (Exception e) {
+			e.printStackTrace();
+			return "update failure";
+		}
+	}
+
+	/**
+	 * 更新路由
+	 */
+	public String updateList(List<RouteDefinition> routeDefinitions) {
+		routeDefinitions.forEach(this::update);
+		return "update done";
+	}
+
+	/**
+	 * 删除路由
+	 */
+	public String delete(String id) {
+		try {
+			this.routeDefinitionWriter.delete(Mono.just(id));
+			return "delete success";
+		} catch (Exception e) {
+			e.printStackTrace();
+			return "delete failure";
+		}
+	}
+
+
+}

+ 94 - 0
blade-gateway/src/main/java/org/springblade/gateway/dynamic/DynamicRouteServiceListener.java

@@ -0,0 +1,94 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.gateway.dynamic;
+
+import com.alibaba.cloud.nacos.NacosConfigProperties;
+import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.nacos.api.NacosFactory;
+import com.alibaba.nacos.api.PropertyKeyConst;
+import com.alibaba.nacos.api.config.ConfigService;
+import com.alibaba.nacos.api.config.listener.Listener;
+import com.alibaba.nacos.api.exception.NacosException;
+import lombok.extern.slf4j.Slf4j;
+import org.springblade.core.launch.constant.NacosConstant;
+import org.springblade.core.launch.props.BladeProperties;
+import org.springframework.cloud.gateway.route.RouteDefinition;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Properties;
+import java.util.concurrent.Executor;
+
+/**
+ * 动态路由监听器
+ *
+ * @author Chill
+ */
+@Order
+@Slf4j
+@Component
+public class DynamicRouteServiceListener {
+
+	private final DynamicRouteService dynamicRouteService;
+	private final NacosDiscoveryProperties nacosDiscoveryProperties;
+	private final NacosConfigProperties nacosConfigProperties;
+	private final BladeProperties bladeProperties;
+
+	public DynamicRouteServiceListener(DynamicRouteService dynamicRouteService, NacosDiscoveryProperties nacosDiscoveryProperties, NacosConfigProperties nacosConfigProperties, BladeProperties bladeProperties) {
+		this.dynamicRouteService = dynamicRouteService;
+		this.nacosDiscoveryProperties = nacosDiscoveryProperties;
+		this.nacosConfigProperties = nacosConfigProperties;
+		this.bladeProperties = bladeProperties;
+		dynamicRouteServiceListener();
+	}
+
+	/**
+	 * 监听Nacos下发的动态路由配置
+	 */
+	private void dynamicRouteServiceListener() {
+		try {
+			String dataId = NacosConstant.dataId(bladeProperties.getName(), bladeProperties.getEnv(), NacosConstant.NACOS_CONFIG_JSON_FORMAT);
+			String group = nacosConfigProperties.getGroup();
+			Properties properties = new Properties();
+			properties.setProperty(PropertyKeyConst.SERVER_ADDR, nacosDiscoveryProperties.getServerAddr());
+			properties.setProperty(PropertyKeyConst.NAMESPACE, nacosDiscoveryProperties.getNamespace());
+			ConfigService configService = NacosFactory.createConfigService(properties);
+			configService.addListener(dataId, group, new Listener() {
+				@Override
+				public void receiveConfigInfo(String configInfo) {
+					List<RouteDefinition> routeDefinitions = JSON.parseArray(configInfo, RouteDefinition.class);
+					dynamicRouteService.updateList(routeDefinitions);
+				}
+
+				@Override
+				public Executor getExecutor() {
+					return null;
+				}
+			});
+			String configInfo = configService.getConfig(dataId, group, 5000);
+			if (configInfo != null) {
+				List<RouteDefinition> routeDefinitions = JSON.parseArray(configInfo, RouteDefinition.class);
+				dynamicRouteService.updateList(routeDefinitions);
+			}
+		} catch (NacosException ignored) {
+
+		}
+	}
+
+}

+ 41 - 0
blade-gateway/src/main/java/org/springblade/gateway/dynamic/GatewayFilter.java

@@ -0,0 +1,41 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.gateway.dynamic;
+
+import lombok.Data;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * 过滤器定义模型
+ *
+ * @author Chill
+ */
+@Data
+public class GatewayFilter {
+
+	/**
+	 * 过滤器对应的Name
+	 */
+	private String name;
+
+	/**
+	 * 对应的路由规则
+	 */
+	private Map<String, String> args = new LinkedHashMap<>();
+}

+ 41 - 0
blade-gateway/src/main/java/org/springblade/gateway/dynamic/GatewayPredicate.java

@@ -0,0 +1,41 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.gateway.dynamic;
+
+import lombok.Data;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * 路由断言定义模型
+ *
+ * @author Chill
+ */
+@Data
+public class GatewayPredicate {
+
+	/**
+	 * 断言对应的Name
+	 */
+	private String name;
+
+	/**
+	 * 配置的断言规则
+	 */
+	private Map<String, String> args = new LinkedHashMap<>();
+}

+ 57 - 0
blade-gateway/src/main/java/org/springblade/gateway/dynamic/GatewayRoute.java

@@ -0,0 +1,57 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.gateway.dynamic;
+
+
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Gateway的路由定义模型
+ *
+ * @author Chill
+ */
+@Data
+public class GatewayRoute {
+
+	/**
+	 * 路由的id
+	 */
+	private String id;
+
+	/**
+	 * 路由断言集合配置
+	 */
+	private List<GatewayPredicate> predicates = new ArrayList<>();
+
+	/**
+	 * 路由过滤器集合配置
+	 */
+	private List<GatewayFilter> filters = new ArrayList<>();
+
+	/**
+	 * 路由规则转发的目标uri
+	 */
+	private String uri;
+
+	/**
+	 * 路由执行的顺序
+	 */
+	private int order = 0;
+}

+ 117 - 0
blade-gateway/src/main/java/org/springblade/gateway/filter/AuthFilter.java

@@ -0,0 +1,117 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.gateway.filter;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.jsonwebtoken.Claims;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang.StringUtils;
+import org.springblade.core.jwt.JwtUtil;
+import org.springblade.core.jwt.props.JwtProperties;
+import org.springblade.core.launch.constant.TokenConstant;
+import org.springblade.gateway.props.AuthProperties;
+import org.springblade.gateway.provider.AuthProvider;
+import org.springblade.gateway.provider.RequestProvider;
+import org.springblade.gateway.provider.ResponseProvider;
+import org.springframework.cloud.gateway.filter.GatewayFilterChain;
+import org.springframework.cloud.gateway.filter.GlobalFilter;
+import org.springframework.core.Ordered;
+import org.springframework.core.io.buffer.DataBuffer;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.server.reactive.ServerHttpResponse;
+import org.springframework.stereotype.Component;
+import org.springframework.util.AntPathMatcher;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+import java.nio.charset.StandardCharsets;
+
+/**
+ * 鉴权认证
+ *
+ * @author Chill
+ */
+@Slf4j
+@Component
+@AllArgsConstructor
+public class AuthFilter implements GlobalFilter, Ordered {
+	private final AuthProperties authProperties;
+	private final ObjectMapper objectMapper;
+	private final JwtProperties jwtProperties;
+	private final AntPathMatcher antPathMatcher = new AntPathMatcher();
+
+	@Override
+	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
+		//校验 Token 放行
+		String originalRequestUrl = RequestProvider.getOriginalRequestUrl(exchange);
+		String path = exchange.getRequest().getURI().getPath();
+		if (isSkip(path) || isSkip(originalRequestUrl)) {
+			return chain.filter(exchange);
+		}
+		//校验 Token 合法性
+		ServerHttpResponse resp = exchange.getResponse();
+		String headerToken = exchange.getRequest().getHeaders().getFirst(AuthProvider.AUTH_KEY);
+		String paramToken = exchange.getRequest().getQueryParams().getFirst(AuthProvider.AUTH_KEY);
+		if (StringUtils.isBlank(headerToken) && StringUtils.isBlank(paramToken)) {
+			return unAuth(resp, "缺失令牌,鉴权失败");
+		}
+		String auth = StringUtils.isBlank(headerToken) ? paramToken : headerToken;
+		String token = JwtUtil.getToken(auth);
+		Claims claims = JwtUtil.parseJWT(token);
+		if (token == null || claims == null) {
+			return unAuth(resp, "请求未授权");
+		}
+		//判断 Token 状态
+		if (jwtProperties.getState()) {
+			String tenantId = String.valueOf(claims.get(TokenConstant.TENANT_ID));
+			String userId = String.valueOf(claims.get(TokenConstant.USER_ID));
+			String accessToken = JwtUtil.getAccessToken(tenantId, userId, token);
+			if (!token.equalsIgnoreCase(accessToken)) {
+				return unAuth(resp, "令牌已失效");
+			}
+		}
+		return chain.filter(exchange);
+	}
+
+	private boolean isSkip(String path) {
+		return AuthProvider.getDefaultSkipUrl().stream().anyMatch(pattern -> antPathMatcher.match(pattern, path))
+			|| authProperties.getSkipUrl().stream().anyMatch(pattern -> antPathMatcher.match(pattern, path));
+	}
+
+	private Mono<Void> unAuth(ServerHttpResponse resp, String msg) {
+		resp.setStatusCode(HttpStatus.UNAUTHORIZED);
+		resp.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
+		String result = "";
+		try {
+			result = objectMapper.writeValueAsString(ResponseProvider.unAuth(msg));
+		} catch (JsonProcessingException e) {
+			log.error(e.getMessage(), e);
+		}
+		DataBuffer buffer = resp.bufferFactory().wrap(result.getBytes(StandardCharsets.UTF_8));
+		return resp.writeWith(Flux.just(buffer));
+	}
+
+
+	@Override
+	public int getOrder() {
+		return -100;
+	}
+
+}

+ 112 - 0
blade-gateway/src/main/java/org/springblade/gateway/filter/GlobalRequestLogFilter.java

@@ -0,0 +1,112 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.gateway.filter;
+
+import io.jsonwebtoken.Claims;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang.StringUtils;
+import org.springblade.core.jwt.JwtUtil;
+import org.springblade.gateway.provider.AuthProvider;
+import org.springblade.gateway.provider.RequestProvider;
+import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.cloud.gateway.filter.GatewayFilterChain;
+import org.springframework.cloud.gateway.filter.GlobalFilter;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.Ordered;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Mono;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * webflux 日志请求记录,方便开发调试。请求日志过滤器排序尽量低。
+ *
+ * <p>
+ * 注意:暂时不支持结构体打印,想实现,请看下面的链接。
+ * https://stackoverflow.com/questions/45240005/how-to-log-request-and-response-bodies-in-spring-webflux
+ * https://github.com/Silvmike/webflux-demo/blob/master/tests/src/test/java/ru/hardcoders/demo/webflux/web_handler/filters/logging
+ * </p>
+ *
+ * @author dream.lu
+ */
+@Slf4j
+@Configuration
+@RequiredArgsConstructor
+@ConditionalOnProperty(value = "blade.log.request.enabled", havingValue = "true", matchIfMissing = true)
+public class GlobalRequestLogFilter implements GlobalFilter, Ordered {
+	private final WebEndpointProperties endpointProperties;
+
+	@Override
+	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
+		ServerHttpRequest request = exchange.getRequest();
+		// 打印请求路径
+		String path = request.getPath().pathWithinApplication().value();
+
+		// 忽略 endpoint 请求
+		String endpointBasePath = endpointProperties.getBasePath();
+		if (StringUtils.isNotBlank(endpointBasePath) && path.startsWith(endpointBasePath)) {
+			return chain.filter(exchange);
+		}
+
+		String requestUrl = RequestProvider.getOriginalRequestUrl(exchange);
+
+		// 构建成一条长 日志,避免并发下日志错乱
+		StringBuilder beforeReqLog = new StringBuilder(300);
+		// 日志参数
+		List<Object> beforeReqArgs = new ArrayList<>();
+		beforeReqLog.append("\n\n================ Gateway Request Start  ================\n");
+		// 打印路由
+		beforeReqLog.append("===> {}: {}\n");
+		// 参数
+		String requestMethod = request.getMethodValue();
+		beforeReqArgs.add(requestMethod);
+		beforeReqArgs.add(requestUrl);
+
+		// 打印请求头
+		HttpHeaders headers = request.getHeaders();
+		headers.forEach((headerName, headerValue) -> {
+			beforeReqLog.append("===Headers===  {}: {}\n");
+			beforeReqArgs.add(headerName);
+			if (AuthProvider.AUTH_KEY.toLowerCase().equals(headerName)) {
+				String value = headerValue.get(0);
+				String token = JwtUtil.getToken(value);
+				Claims claims = JwtUtil.parseJWT(token);
+				beforeReqArgs.add((claims == null) ? "" : claims.toString());
+				beforeReqLog.append("===Headers===  {}: {}\n");
+				beforeReqArgs.add(headerName.concat("-original"));
+				beforeReqArgs.add(StringUtils.join(headerValue.toArray()));
+			} else {
+				beforeReqArgs.add(StringUtils.join(headerValue.toArray()));
+			}
+		});
+
+		beforeReqLog.append("================  Gateway Request End  =================\n");
+		// 打印执行时间
+		log.info(beforeReqLog.toString(), beforeReqArgs.toArray());
+		return chain.filter(exchange);
+	}
+
+	@Override
+	public int getOrder() {
+		return Ordered.LOWEST_PRECEDENCE;
+	}
+}

+ 99 - 0
blade-gateway/src/main/java/org/springblade/gateway/filter/GlobalResponseLogFilter.java

@@ -0,0 +1,99 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.gateway.filter;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang.StringUtils;
+import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.cloud.gateway.filter.GatewayFilterChain;
+import org.springframework.cloud.gateway.filter.GlobalFilter;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.Ordered;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.http.server.reactive.ServerHttpResponse;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.server.ServerWebExchange;
+import org.springframework.web.util.UriComponentsBuilder;
+import reactor.core.publisher.Mono;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * webflux 相应日志,方便开发调试,注意排序要优先。
+ *
+ * @author dream.lu
+ */
+@Slf4j
+@Configuration
+@RequiredArgsConstructor
+@ConditionalOnProperty(value = "blade.log.request.enabled", havingValue = "true", matchIfMissing = true)
+public class GlobalResponseLogFilter implements GlobalFilter, Ordered {
+	private final WebEndpointProperties endpointProperties;
+
+	@Override
+	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
+		ServerHttpRequest request = exchange.getRequest();
+		// 打印请求路径
+		String path = request.getPath().pathWithinApplication().value();
+		// 忽略 endpoint 请求
+		String endpointBasePath = endpointProperties.getBasePath();
+		if (StringUtils.isNotBlank(endpointBasePath) && path.startsWith(endpointBasePath)) {
+			return chain.filter(exchange);
+		}
+		return chain.filter(exchange).then(
+			Mono.fromRunnable(() -> {
+				MultiValueMap<String, String> queryParams = request.getQueryParams();
+				String requestUrl = UriComponentsBuilder.fromPath(path).queryParams(queryParams).build().toUriString();
+
+				// 构建成一条长 日志,避免并发下日志错乱
+				StringBuilder responseLog = new StringBuilder(300);
+				// 日志参数
+				List<Object> responseArgs = new ArrayList<>();
+				responseLog.append("\n\n================ Gateway Response Start  ================\n");
+				ServerHttpResponse response = exchange.getResponse();
+				// 打印路由 200 get: /api/xxx/xxx
+				responseLog.append("<=== {} {}: {}\n");
+				// 参数
+				String requestMethod = request.getMethodValue();
+				responseArgs.add(response.getStatusCode().value());
+				responseArgs.add(requestMethod);
+				responseArgs.add(requestUrl);
+
+				// 打印请求头
+				HttpHeaders headers = response.getHeaders();
+				headers.forEach((headerName, headerValue) -> {
+					responseLog.append("===Headers===  {}: {}\n");
+					responseArgs.add(headerName);
+					responseArgs.add(StringUtils.join(headerValue.toArray()));
+				});
+
+				responseLog.append("================  Gateway Response End  =================\n");
+				// 打印执行时间
+				log.info(responseLog.toString(), responseArgs.toArray());
+			})
+		);
+	}
+
+	@Override
+	public int getOrder() {
+		return Ordered.HIGHEST_PRECEDENCE;
+	}
+}

+ 63 - 0
blade-gateway/src/main/java/org/springblade/gateway/filter/RequestFilter.java

@@ -0,0 +1,63 @@
+package org.springblade.gateway.filter;
+
+import org.springframework.cloud.gateway.filter.GatewayFilterChain;
+import org.springframework.cloud.gateway.filter.GlobalFilter;
+import org.springframework.core.Ordered;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Mono;
+
+import java.util.Arrays;
+import java.util.stream.Collectors;
+
+import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
+import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.addOriginalRequestUrl;
+
+/**
+ * <p>
+ * 全局拦截器,作用所有的微服务
+ * <p>
+ * 1. 对请求头中参数进行处理 from 参数进行清洗
+ * 2. 重写StripPrefix = 1,支持全局
+ *
+ * @author lengleng
+ */
+@Component
+public class RequestFilter implements GlobalFilter, Ordered {
+
+	/**
+	 * Process the Web request and (optionally) delegate to the next
+	 * {@code WebFilter} through the given {@link GatewayFilterChain}.
+	 *
+	 * @param exchange the current server exchange
+	 * @param chain    provides a way to delegate to the next filter
+	 * @return {@code Mono<Void>} to indicate when request processing is complete
+	 */
+	@Override
+	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
+		// 1. 清洗请求头中from 参数
+		ServerHttpRequest request = exchange.getRequest().mutate()
+			.headers(httpHeaders -> httpHeaders.remove("X"))
+			.build();
+
+		// 2. 重写StripPrefix
+		addOriginalRequestUrl(exchange, request.getURI());
+		String rawPath = request.getURI().getRawPath();
+		String newPath = "/" + Arrays.stream(StringUtils.tokenizeToStringArray(rawPath, "/"))
+			.skip(1L).collect(Collectors.joining("/"));
+		ServerHttpRequest newRequest = request.mutate()
+			.path(newPath)
+			.build();
+		exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, newRequest.getURI());
+
+		return chain.filter(exchange.mutate().request(newRequest.mutate().build()).build());
+	}
+
+	@Override
+	public int getOrder() {
+		return -1000;
+	}
+
+}

+ 96 - 0
blade-gateway/src/main/java/org/springblade/gateway/handler/ErrorExceptionHandler.java

@@ -0,0 +1,96 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.gateway.handler;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.RequiredArgsConstructor;
+import org.springblade.gateway.provider.ResponseProvider;
+import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
+import org.springframework.core.annotation.Order;
+import org.springframework.core.io.buffer.DataBufferFactory;
+import org.springframework.http.MediaType;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.http.server.reactive.ServerHttpResponse;
+import org.springframework.web.server.ResponseStatusException;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Mono;
+
+import java.util.Map;
+
+/**
+ * 异常处理
+ *
+ * @author Chill
+ */
+@Order(-1)
+@RequiredArgsConstructor
+public class ErrorExceptionHandler implements ErrorWebExceptionHandler {
+
+	private final ObjectMapper objectMapper;
+
+	@Override
+	public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
+		ServerHttpRequest request = exchange.getRequest();
+		ServerHttpResponse response = exchange.getResponse();
+
+		if (response.isCommitted()) {
+			return Mono.error(ex);
+		}
+
+		response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
+		if (ex instanceof ResponseStatusException) {
+			response.setStatusCode(((ResponseStatusException) ex).getStatus());
+		}
+
+		return response.writeWith(Mono.fromSupplier(() -> {
+			DataBufferFactory bufferFactory = response.bufferFactory();
+			try {
+				int status = 500;
+				if (response.getStatusCode() != null) {
+					status = response.getStatusCode().value();
+				}
+				Map<String, Object> result = ResponseProvider.response(status, this.buildMessage(request, ex));
+				return bufferFactory.wrap(objectMapper.writeValueAsBytes(result));
+			} catch (JsonProcessingException e) {
+				return bufferFactory.wrap(new byte[0]);
+			}
+		}));
+	}
+
+
+	/**
+	 * 构建异常信息
+	 */
+	private String buildMessage(ServerHttpRequest request, Throwable ex) {
+		String uri = request.getURI().toString();
+		if (uri.endsWith("doc.html")) {
+			return "[Swagger聚合网关] 已迁移至 [blade-swagger] 服务,请开启 [blade-swagger] 服务并访问 [http://127.0.0.1:18000/doc.html]";
+		}
+		StringBuilder message = new StringBuilder("Failed to handle request [");
+		message.append(request.getMethodValue());
+		message.append(" ");
+		message.append(request.getURI());
+		message.append("]");
+		if (ex != null) {
+			message.append(": ");
+			message.append(ex.getMessage());
+		}
+		return message.toString();
+	}
+
+}

+ 41 - 0
blade-gateway/src/main/java/org/springblade/gateway/props/AuthProperties.java

@@ -0,0 +1,41 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.gateway.props;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 权限过滤
+ *
+ * @author Chill
+ */
+@Data
+@RefreshScope
+@ConfigurationProperties("blade.secure")
+public class AuthProperties {
+
+	/**
+	 * 放行API集合
+	 */
+	private final List<String> skipUrl = new ArrayList<>();
+
+}

+ 67 - 0
blade-gateway/src/main/java/org/springblade/gateway/provider/AuthProvider.java

@@ -0,0 +1,67 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.gateway.provider;
+
+import org.springblade.core.launch.constant.TokenConstant;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 鉴权配置
+ *
+ * @author Chill
+ */
+public class AuthProvider {
+
+	public static final String AUTH_KEY = TokenConstant.HEADER;
+	private static final List<String> DEFAULT_SKIP_URL = new ArrayList<>();
+
+	static {
+		DEFAULT_SKIP_URL.add("/example");
+		DEFAULT_SKIP_URL.add("/oauth/token/**");
+		DEFAULT_SKIP_URL.add("/oauth/captcha/**");
+		DEFAULT_SKIP_URL.add("/oauth/clear-cache/**");
+		DEFAULT_SKIP_URL.add("/oauth/user-info");
+		DEFAULT_SKIP_URL.add("/oauth/render/**");
+		DEFAULT_SKIP_URL.add("/oauth/callback/**");
+		DEFAULT_SKIP_URL.add("/oauth/revoke/**");
+		DEFAULT_SKIP_URL.add("/oauth/refresh/**");
+		DEFAULT_SKIP_URL.add("/token/**");
+		DEFAULT_SKIP_URL.add("/actuator/**");
+		DEFAULT_SKIP_URL.add("/v2/api-docs/**");
+		DEFAULT_SKIP_URL.add("/auth/**");
+		DEFAULT_SKIP_URL.add("/log/**");
+		DEFAULT_SKIP_URL.add("/menu/routes");
+		DEFAULT_SKIP_URL.add("/menu/auth-routes");
+		DEFAULT_SKIP_URL.add("/menu/top-menu");
+		DEFAULT_SKIP_URL.add("/tenant/info");
+		DEFAULT_SKIP_URL.add("/process/resource-view");
+		DEFAULT_SKIP_URL.add("/process/diagram-view");
+		DEFAULT_SKIP_URL.add("/manager/check-upload");
+		DEFAULT_SKIP_URL.add("/error/**");
+		DEFAULT_SKIP_URL.add("/assets/**");
+	}
+
+	/**
+	 * 默认无需鉴权的API
+	 */
+	public static List<String> getDefaultSkipUrl() {
+		return DEFAULT_SKIP_URL;
+	}
+
+}

+ 49 - 0
blade-gateway/src/main/java/org/springblade/gateway/provider/RequestProvider.java

@@ -0,0 +1,49 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.gateway.provider;
+
+import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.server.ServerWebExchange;
+import org.springframework.web.util.UriComponentsBuilder;
+
+import java.net.URI;
+import java.util.LinkedHashSet;
+
+/**
+ * RequestProvider
+ *
+ * @author Chill
+ */
+public class RequestProvider {
+
+	/**
+	 * 获取原始url
+	 *
+	 * @param exchange
+	 * @return
+	 */
+	public static String getOriginalRequestUrl(ServerWebExchange exchange) {
+		ServerHttpRequest request = exchange.getRequest();
+		LinkedHashSet<URI> uris = exchange.getRequiredAttribute(ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR);
+		URI requestUri = uris.stream().findFirst().orElse(request.getURI());
+		MultiValueMap<String, String> queryParams = request.getQueryParams();
+		return UriComponentsBuilder.fromPath(requestUri.getRawPath()).queryParams(queryParams).build().toUriString();
+	}
+
+}

+ 84 - 0
blade-gateway/src/main/java/org/springblade/gateway/provider/ResponseProvider.java

@@ -0,0 +1,84 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.gateway.provider;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 请求响应返回
+ *
+ * @author Chill
+ */
+public class ResponseProvider {
+
+	/**
+	 * 成功
+	 *
+	 * @param message 信息
+	 * @return
+	 */
+	public static Map<String, Object> success(String message) {
+		return response(200, message);
+	}
+
+	/**
+	 * 失败
+	 *
+	 * @param message 信息
+	 * @return
+	 */
+	public static Map<String, Object> fail(String message) {
+		return response(400, message);
+	}
+
+	/**
+	 * 未授权
+	 *
+	 * @param message 信息
+	 * @return
+	 */
+	public static Map<String, Object> unAuth(String message) {
+		return response(401, message);
+	}
+
+	/**
+	 * 服务器异常
+	 *
+	 * @param message 信息
+	 * @return
+	 */
+	public static Map<String, Object> error(String message) {
+		return response(500, message);
+	}
+
+	/**
+	 * 构建返回的JSON数据格式
+	 *
+	 * @param status  状态码
+	 * @param message 信息
+	 * @return
+	 */
+	public static Map<String, Object> response(int status, String message) {
+		Map<String, Object> map = new HashMap<>(16);
+		map.put("code", status);
+		map.put("msg", message);
+		map.put("data", null);
+		return map;
+	}
+
+}

+ 10 - 0
blade-gateway/src/main/resources/application-dev.yml

@@ -0,0 +1,10 @@
+blade:
+  #多团队协作服务配置
+  ribbon:
+    rule:
+      #开启配置
+      enabled: true
+      #负载均衡优先调用的ip段
+      prior-ip-pattern:
+        - 192.168.0.*
+        - 127.0.0.1

+ 15 - 0
blade-gateway/src/main/resources/bootstrap.yml

@@ -0,0 +1,15 @@
+server:
+  port: 80
+
+spring:
+  cloud:
+    discovery:
+      reactive:
+        enabled: false
+    gateway:
+      discovery:
+        locator:
+          enabled: true
+    loadbalancer:
+      retry:
+        enabled: true

+ 162 - 0
blade-ops-api/blade-flow-api/blade-flow-api.iml

@@ -0,0 +1,162 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
+  <component name="FacetManager">
+    <facet type="Spring" name="Spring">
+      <configuration />
+    </facet>
+    <facet type="web" name="Web">
+      <configuration>
+        <webroots />
+      </configuration>
+    </facet>
+  </component>
+  <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
+    <output url="file://$MODULE_DIR$/target/classes" />
+    <output-test url="file://$MODULE_DIR$/target/test-classes" />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
+      <excludeFolder url="file://$MODULE_DIR$/target" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-starter-mybatis:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.baomidou:mybatis-plus:3.5.1" level="project" />
+    <orderEntry type="library" name="Maven: com.baomidou:mybatis-plus-extension:3.5.1" level="project" />
+    <orderEntry type="library" name="Maven: com.baomidou:mybatis-plus-core:3.5.1" level="project" />
+    <orderEntry type="library" name="Maven: com.baomidou:mybatis-plus-annotation:3.5.1" level="project" />
+    <orderEntry type="library" name="Maven: com.github.jsqlparser:jsqlparser:4.3" level="project" />
+    <orderEntry type="library" name="Maven: org.mybatis:mybatis:3.5.9" level="project" />
+    <orderEntry type="library" name="Maven: org.mybatis:mybatis-spring:2.0.6" level="project" />
+    <orderEntry type="library" name="Maven: org.mybatis:mybatis-typehandlers-jsr310:1.0.2" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-jdbc:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-context:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-expression:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-logging:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: ch.qos.logback:logback-classic:1.2.11" level="project" />
+    <orderEntry type="library" name="Maven: ch.qos.logback:logback-core:1.2.11" level="project" />
+    <orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-to-slf4j:2.17.2" level="project" />
+    <orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-api:2.17.2" level="project" />
+    <orderEntry type="library" name="Maven: org.slf4j:jul-to-slf4j:1.7.30" level="project" />
+    <orderEntry type="library" name="Maven: jakarta.annotation:jakarta.annotation-api:1.3.5" level="project" />
+    <orderEntry type="library" name="Maven: org.yaml:snakeyaml:1.26" level="project" />
+    <orderEntry type="library" name="Maven: com.zaxxer:HikariCP:3.4.5" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-jdbc:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-tx:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba:druid:1.2.8" level="project" />
+    <orderEntry type="library" name="Maven: javax.annotation:javax.annotation-api:1.3.2" level="project" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-starter-auth:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-core-tool:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-core-launch:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-web:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-json:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.module:jackson-module-parameter-names:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-webmvc:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-undertow:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: io.undertow:undertow-core:2.1.7.Final" level="project" />
+    <orderEntry type="library" name="Maven: org.jboss.xnio:xnio-api:3.8.0.Final" level="project" />
+    <orderEntry type="library" name="Maven: org.wildfly.common:wildfly-common:1.5.2.Final" level="project" />
+    <orderEntry type="library" name="Maven: org.wildfly.client:wildfly-client-config:1.0.1.Final" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: org.jboss.xnio:xnio-nio:3.8.0.Final" level="project" />
+    <orderEntry type="library" name="Maven: org.jboss.threads:jboss-threads:3.1.0.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.undertow:undertow-servlet:2.1.7.Final" level="project" />
+    <orderEntry type="library" name="Maven: org.jboss.spec.javax.annotation:jboss-annotations-api_1.3_spec:2.0.1.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.undertow:undertow-websockets-jsr:2.1.7.Final" level="project" />
+    <orderEntry type="library" name="Maven: org.jboss.spec.javax.websocket:jboss-websocket-api_1.1_spec:2.0.0.Final" level="project" />
+    <orderEntry type="library" name="Maven: jakarta.servlet:jakarta.servlet-api:4.0.4" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: com.google.guava:guava:30.1.1-jre" level="project" />
+    <orderEntry type="library" name="Maven: com.google.guava:failureaccess:1.0.1" level="project" />
+    <orderEntry type="library" name="Maven: com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava" level="project" />
+    <orderEntry type="library" name="Maven: org.checkerframework:checker-qual:3.8.0" level="project" />
+    <orderEntry type="library" name="Maven: com.google.errorprone:error_prone_annotations:2.5.1" level="project" />
+    <orderEntry type="library" name="Maven: com.google.j2objc:j2objc-annotations:1.3" level="project" />
+    <orderEntry type="library" name="Maven: io.springfox:springfox-swagger2:2.10.5" level="project" />
+    <orderEntry type="library" name="Maven: io.swagger:swagger-annotations:1.5.20" level="project" />
+    <orderEntry type="library" name="Maven: io.springfox:springfox-spi:2.10.5" level="project" />
+    <orderEntry type="library" name="Maven: io.springfox:springfox-core:2.10.5" level="project" />
+    <orderEntry type="library" name="Maven: net.bytebuddy:byte-buddy:1.10.22" level="project" />
+    <orderEntry type="library" name="Maven: io.springfox:springfox-schema:2.10.5" level="project" />
+    <orderEntry type="library" name="Maven: io.springfox:springfox-swagger-common:2.10.5" level="project" />
+    <orderEntry type="library" name="Maven: io.springfox:springfox-spring-web:2.10.5" level="project" />
+    <orderEntry type="library" name="Maven: io.github.classgraph:classgraph:4.1.7" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml:classmate:1.5.1" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.plugin:spring-plugin-core:2.0.0.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.plugin:spring-plugin-metadata:2.0.0.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.mapstruct:mapstruct:1.3.1.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.swagger:swagger-models:1.6.2" level="project" />
+    <orderEntry type="library" name="Maven: io.protostuff:protostuff-core:1.6.0" level="project" />
+    <orderEntry type="library" name="Maven: io.protostuff:protostuff-api:1.6.0" level="project" />
+    <orderEntry type="library" name="Maven: io.protostuff:protostuff-runtime:1.6.0" level="project" />
+    <orderEntry type="library" name="Maven: io.protostuff:protostuff-collectionschema:1.6.0" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.module:jackson-module-jaxb-annotations:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: jakarta.xml.bind:jakarta.xml.bind-api:2.3.3" level="project" />
+    <orderEntry type="library" name="Maven: jakarta.activation:jakarta.activation-api:1.2.2" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-validation:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.glassfish:jakarta.el:3.0.3" level="project" />
+    <orderEntry type="library" name="Maven: org.hibernate.validator:hibernate-validator:6.1.7.Final" level="project" />
+    <orderEntry type="library" name="Maven: jakarta.validation:jakarta.validation-api:2.0.2" level="project" />
+    <orderEntry type="library" name="Maven: org.jboss.logging:jboss-logging:3.4.2.Final" level="project" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-starter-jwt:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-data-redis:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.data:spring-data-redis:2.3.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.data:spring-data-keyvalue:2.3.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.data:spring-data-commons:2.3.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-oxm:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-context-support:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: io.lettuce:lettuce-core:5.3.7.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-common:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-handler:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-resolver:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-buffer:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-codec:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-transport:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.projectreactor:reactor-core:3.3.17.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.reactivestreams:reactive-streams:1.0.3" level="project" />
+    <orderEntry type="library" name="Maven: io.jsonwebtoken:jjwt-impl:0.11.2" level="project" />
+    <orderEntry type="library" name="Maven: io.jsonwebtoken:jjwt-api:0.11.2" level="project" />
+    <orderEntry type="library" name="Maven: io.jsonwebtoken:jjwt-jackson:0.11.2" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-aop:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-aop:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.aspectj:aspectjweaver:1.9.6" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.retry:spring-retry:1.2.5.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-core:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-jcl:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-starter-openfeign:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-starter:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-context:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.security:spring-security-rsa:1.0.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.bouncycastle:bcpkix-jdk15on:1.59" level="project" />
+    <orderEntry type="library" name="Maven: org.bouncycastle:bcprov-jdk15on:1.59" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-openfeign-core:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-autoconfigure:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-netflix-ribbon:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-netflix-archaius:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: io.github.openfeign.form:feign-form-spring:3.8.0" level="project" />
+    <orderEntry type="library" name="Maven: io.github.openfeign.form:feign-form:3.8.0" level="project" />
+    <orderEntry type="library" name="Maven: commons-fileupload:commons-fileupload:1.3.3" level="project" />
+    <orderEntry type="library" name="Maven: commons-io:commons-io:2.6" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-web:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-beans:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-commons:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.security:spring-security-crypto:5.3.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: io.github.openfeign:feign-core:10.12" level="project" />
+    <orderEntry type="library" name="Maven: io.github.openfeign:feign-slf4j:10.12" level="project" />
+    <orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.7.30" level="project" />
+    <orderEntry type="library" name="Maven: io.github.openfeign:feign-hystrix:10.12" level="project" />
+    <orderEntry type="library" name="Maven: com.netflix.archaius:archaius-core:0.7.7" level="project" />
+    <orderEntry type="library" name="Maven: com.google.code.findbugs:jsr305:3.0.2" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: commons-configuration:commons-configuration:1.8" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: commons-lang:commons-lang:2.6" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-core:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-databind:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: com.netflix.hystrix:hystrix-core:1.5.18" level="project" />
+    <orderEntry type="library" name="Maven: io.reactivex:rxjava:1.3.8" level="project" />
+    <orderEntry type="library" name="Maven: org.hdrhistogram:HdrHistogram:2.1.9" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.springblade:blade-core-auto:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.projectlombok:lombok:1.18.22" level="project" />
+  </component>
+</module>

+ 19 - 0
blade-ops-api/blade-flow-api/pom.xml

@@ -0,0 +1,19 @@
+<?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>blade-ops-api</artifactId>
+        <groupId>org.springblade</groupId>
+        <version>2.9.1.RELEASE</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>blade-flow-api</artifactId>
+    <name>${project.artifactId}</name>
+    <version>${bladex.project.version}</version>
+    <packaging>jar</packaging>
+
+
+
+</project>

+ 61 - 0
blade-ops-api/blade-flow-api/src/main/java/org/springblade/flow/core/constant/ProcessConstant.java

@@ -0,0 +1,61 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.flow.core.constant;
+
+/**
+ * 流程常量.
+ *
+ * @author Chill
+ */
+public interface ProcessConstant {
+
+	/**
+	 * 请假流程标识
+	 */
+	String LEAVE_KEY = "Leave";
+
+	/**
+	 * 报销流程标识
+	 */
+	String EXPENSE_KEY = "Expense";
+
+	/**
+	 * 同意标识
+	 */
+	String PASS_KEY = "pass";
+
+	/**
+	 * 同意代号
+	 */
+	String PASS_ALIAS = "ok";
+
+	/**
+	 * 同意默认批复
+	 */
+	String PASS_COMMENT = "同意";
+
+	/**
+	 * 驳回默认批复
+	 */
+	String NOT_PASS_COMMENT = "驳回";
+
+	/**
+	 * 创建人变量名
+	 */
+	String TASK_VARIABLE_CREATE_USER = "createUser";
+
+}

+ 179 - 0
blade-ops-api/blade-flow-api/src/main/java/org/springblade/flow/core/entity/BladeFlow.java

@@ -0,0 +1,179 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.flow.core.entity;
+
+import lombok.Data;
+import org.springblade.flow.core.constant.ProcessConstant;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.Map;
+
+/**
+ * 工作流通用实体类
+ *
+ * @author Chill
+ */
+@Data
+public class BladeFlow implements Serializable {
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * 任务编号
+	 */
+	private String taskId;
+	/**
+	 * 任务名称
+	 */
+	private String taskName;
+	/**
+	 * 任务定义Key
+	 */
+	private String taskDefinitionKey;
+	/**
+	 * 任务执行人编号
+	 */
+	private String assignee;
+	/**
+	 * 任务执行人名称
+	 */
+	private String assigneeName;
+	/**
+	 * 流程分类
+	 */
+	private String category;
+	/**
+	 * 流程分类名
+	 */
+	private String categoryName;
+	/**
+	 * 创建时间
+	 */
+	private Date createTime;
+	/**
+	 * 结束时间
+	 */
+	private Date endTime;
+	/**
+	 * 签收时间
+	 */
+	private Date claimTime;
+	/**
+	 * 历史任务结束时间
+	 */
+	private Date historyTaskEndTime;
+	/**
+	 * 执行ID
+	 */
+	private String executionId;
+	/**
+	 * 流程实例ID
+	 */
+	private String processInstanceId;
+	/**
+	 * 流程ID
+	 */
+	private String processDefinitionId;
+	/**
+	 * 流程标识
+	 */
+	private String processDefinitionKey;
+	/**
+	 * 流程名
+	 */
+	private String processDefinitionName;
+	/**
+	 * 流程版本
+	 */
+	private int processDefinitionVersion;
+	/**
+	 * 流程说明
+	 */
+	private String processDefinitionDesc;
+	/**
+	 * 流程简图名
+	 */
+	private String processDefinitionDiagramResName;
+	/**
+	 * 流程重命名
+	 */
+	private String processDefinitionResName;
+	/**
+	 * 历史任务流程实例ID 查看流程图会用到
+	 */
+	private String historyProcessInstanceId;
+	/**
+	 * 流程实例是否结束
+	 */
+	private String processIsFinished;
+	/**
+	 * 历史活动ID
+	 */
+	private String historyActivityId;
+	/**
+	 * 历史活动流程
+	 */
+	private String historyActivityName;
+	/**
+	 * 历史活动耗时
+	 */
+	private String historyActivityDurationTime;
+	/**
+	 * 业务绑定Table
+	 */
+	private String businessTable;
+	/**
+	 * 业务绑定ID
+	 */
+	private String businessId;
+	/**
+	 * 任务状态
+	 */
+	private String status;
+	/**
+	 * 任务意见
+	 */
+	private String comment;
+	/**
+	 * 是否通过
+	 */
+	private boolean isPass;
+	/**
+	 * 是否通过代号
+	 */
+	private String flag;
+	/**
+	 * 开始查询日期
+	 */
+	private Date beginDate;
+	/**
+	 * 结束查询日期
+	 */
+	private Date endDate;
+	/**
+	 * 流程参数
+	 */
+	private Map<String, Object> variables;
+
+	/**
+	 * 获取是否通过
+	 */
+	public boolean isPass() {
+		return ProcessConstant.PASS_ALIAS.equals(flag) || ProcessConstant.PASS_COMMENT.equals(comment);
+	}
+
+}

+ 43 - 0
blade-ops-api/blade-flow-api/src/main/java/org/springblade/flow/core/entity/FlowEntity.java

@@ -0,0 +1,43 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.flow.core.entity;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springblade.core.mp.base.BaseEntity;
+
+/**
+ * FlowEntity
+ *
+ * @author Chill
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class FlowEntity extends BaseEntity {
+
+	@TableField(exist = false)
+	private BladeFlow flow;
+
+	public BladeFlow getFlow() {
+		if (flow == null) {
+			flow = new BladeFlow();
+		}
+		return flow;
+	}
+
+}

+ 45 - 0
blade-ops-api/blade-flow-api/src/main/java/org/springblade/flow/core/enums/FlowModeEnum.java

@@ -0,0 +1,45 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.flow.core.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 流程类型枚举
+ *
+ * @author Chill
+ */
+@Getter
+@AllArgsConstructor
+public enum FlowModeEnum {
+
+	/**
+	 * 通用流程
+	 */
+	COMMON("common", 1),
+
+	/**
+	 * 定制流程
+	 */
+	CUSTOM("custom", 2),
+	;
+
+	final String name;
+	final int mode;
+
+}

+ 100 - 0
blade-ops-api/blade-flow-api/src/main/java/org/springblade/flow/core/feign/IFlowClient.java

@@ -0,0 +1,100 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.flow.core.feign;
+
+import org.springblade.core.launch.constant.AppConstant;
+import org.springblade.core.tool.api.R;
+import org.springblade.flow.core.entity.BladeFlow;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestParam;
+
+import java.util.Map;
+
+/**
+ * 工作流远程调用接口.
+ *
+ * @author Chill
+ */
+@FeignClient(
+	value = AppConstant.APPLICATION_FLOW_NAME,
+	fallback = IFlowClientFallback.class
+)
+public interface IFlowClient {
+
+	String API_PREFIX = "/client";
+	String START_PROCESS_INSTANCE_BY_ID = API_PREFIX + "/start-process-instance-by-id";
+	String START_PROCESS_INSTANCE_BY_KEY = API_PREFIX + "/start-process-instance-by-key";
+	String COMPLETE_TASK = API_PREFIX + "/complete-task";
+	String TASK_VARIABLE = API_PREFIX + "/task-variable";
+	String TASK_VARIABLES = API_PREFIX + "/task-variables";
+
+	/**
+	 * 开启流程
+	 *
+	 * @param processDefinitionId 流程id
+	 * @param businessKey         业务key
+	 * @param variables           参数
+	 * @return BladeFlow
+	 */
+	@PostMapping(START_PROCESS_INSTANCE_BY_ID)
+	R<BladeFlow> startProcessInstanceById(@RequestParam("processDefinitionId") String processDefinitionId, @RequestParam("businessKey") String businessKey, @RequestBody Map<String, Object> variables);
+
+	/**
+	 * 开启流程
+	 *
+	 * @param processDefinitionKey 流程标识
+	 * @param businessKey          业务key
+	 * @param variables            参数
+	 * @return BladeFlow
+	 */
+	@PostMapping(START_PROCESS_INSTANCE_BY_KEY)
+	R<BladeFlow> startProcessInstanceByKey(@RequestParam("processDefinitionKey") String processDefinitionKey, @RequestParam("businessKey") String businessKey, @RequestBody Map<String, Object> variables);
+
+	/**
+	 * 完成任务
+	 *
+	 * @param taskId            任务id
+	 * @param processInstanceId 流程实例id
+	 * @param comment           评论
+	 * @param variables         参数
+	 * @return R
+	 */
+	@PostMapping(COMPLETE_TASK)
+	R completeTask(@RequestParam("taskId") String taskId, @RequestParam("processInstanceId") String processInstanceId, @RequestParam("comment") String comment, @RequestBody Map<String, Object> variables);
+
+	/**
+	 * 获取流程变量
+	 *
+	 * @param taskId       任务id
+	 * @param variableName 变量名
+	 * @return R
+	 */
+	@GetMapping(TASK_VARIABLE)
+	R<Object> taskVariable(@RequestParam("taskId") String taskId, @RequestParam("variableName") String variableName);
+
+	/**
+	 * 获取流程变量集合
+	 *
+	 * @param taskId 任务id
+	 * @return R
+	 */
+	@GetMapping(TASK_VARIABLES)
+	R<Map<String, Object>> taskVariables(@RequestParam("taskId") String taskId);
+}

+ 58 - 0
blade-ops-api/blade-flow-api/src/main/java/org/springblade/flow/core/feign/IFlowClientFallback.java

@@ -0,0 +1,58 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.flow.core.feign;
+
+import org.springblade.core.tool.api.R;
+import org.springblade.flow.core.entity.BladeFlow;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+
+/**
+ * 流程远程调用失败处理类
+ *
+ * @author Chill
+ */
+@Component
+public class IFlowClientFallback implements IFlowClient {
+
+	@Override
+	public R<BladeFlow> startProcessInstanceById(String processDefinitionId, String businessKey, Map<String, Object> variables) {
+		return R.fail("远程调用失败");
+	}
+
+	@Override
+	public R<BladeFlow> startProcessInstanceByKey(String processDefinitionKey, String businessKey, Map<String, Object> variables) {
+		return R.fail("远程调用失败");
+	}
+
+	@Override
+	public R completeTask(String taskId, String processInstanceId, String comment, Map<String, Object> variables) {
+		return R.fail("远程调用失败");
+	}
+
+	@Override
+	public R<Object> taskVariable(String taskId, String variableName) {
+		return R.fail("远程调用失败");
+	}
+
+	@Override
+	public R<Map<String, Object>> taskVariables(String taskId) {
+		return R.fail("远程调用失败");
+	}
+
+}

+ 66 - 0
blade-ops-api/blade-flow-api/src/main/java/org/springblade/flow/core/utils/FlowUtil.java

@@ -0,0 +1,66 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.flow.core.utils;
+
+import org.springblade.core.tool.utils.Func;
+import org.springblade.core.tool.utils.StringUtil;
+import org.springblade.flow.core.constant.ProcessConstant;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 工作流工具类
+ *
+ * @author Chill
+ */
+public class FlowUtil {
+
+	/**
+	 * 定义流程key对应的表名
+	 */
+	private final static Map<String, String> BUSINESS_TABLE = new HashMap<>();
+
+	static {
+		BUSINESS_TABLE.put(ProcessConstant.LEAVE_KEY, "blade_process_leave");
+	}
+
+	/**
+	 * 通过流程key获取业务表名
+	 *
+	 * @param key 流程key
+	 */
+	public static String getBusinessTable(String key) {
+		String businessTable = BUSINESS_TABLE.get(key);
+		if (Func.isEmpty(businessTable)) {
+			throw new RuntimeException("流程启动失败,未找到相关业务表");
+		}
+		return businessTable;
+	}
+
+	/**
+	 * 获取业务标识
+	 *
+	 * @param businessTable 业务表
+	 * @param businessId    业务表主键
+	 * @return businessKey
+	 */
+	public static String getBusinessKey(String businessTable, String businessId) {
+		return StringUtil.format("{}:{}", businessTable, businessId);
+	}
+
+}

+ 71 - 0
blade-ops-api/blade-flow-api/src/main/java/org/springblade/flow/core/utils/TaskUtil.java

@@ -0,0 +1,71 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.flow.core.utils;
+
+import org.springblade.core.secure.utils.AuthUtil;
+import org.springblade.core.tool.utils.Func;
+import org.springblade.core.tool.utils.StringUtil;
+
+import static org.springblade.core.launch.constant.FlowConstant.TASK_USR_PREFIX;
+
+/**
+ * 工作流任务工具类
+ *
+ * @author Chill
+ */
+public class TaskUtil {
+
+	/**
+	 * 获取任务用户格式
+	 *
+	 * @return taskUser
+	 */
+	public static String getTaskUser() {
+		return StringUtil.format("{}{}", TASK_USR_PREFIX, AuthUtil.getUserId());
+	}
+
+	/**
+	 * 获取任务用户格式
+	 *
+	 * @param userId 用户id
+	 * @return taskUser
+	 */
+	public static String getTaskUser(String userId) {
+		return StringUtil.format("{}{}", TASK_USR_PREFIX, userId);
+	}
+
+
+	/**
+	 * 获取用户主键
+	 *
+	 * @param taskUser 任务用户
+	 * @return userId
+	 */
+	public static Long getUserId(String taskUser) {
+		return Func.toLong(StringUtil.removePrefix(taskUser, TASK_USR_PREFIX));
+	}
+
+	/**
+	 * 获取用户组格式
+	 *
+	 * @return candidateGroup
+	 */
+	public static String getCandidateGroup() {
+		return AuthUtil.getUserRole();
+	}
+
+}

+ 151 - 0
blade-ops-api/blade-ops-api.iml

@@ -0,0 +1,151 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
+    <output url="file://$MODULE_DIR$/target/classes" />
+    <output-test url="file://$MODULE_DIR$/target/test-classes" />
+    <content url="file://$MODULE_DIR$">
+      <excludeFolder url="file://$MODULE_DIR$/target" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-starter-mybatis:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.baomidou:mybatis-plus:3.5.1" level="project" />
+    <orderEntry type="library" name="Maven: com.baomidou:mybatis-plus-extension:3.5.1" level="project" />
+    <orderEntry type="library" name="Maven: com.baomidou:mybatis-plus-core:3.5.1" level="project" />
+    <orderEntry type="library" name="Maven: com.baomidou:mybatis-plus-annotation:3.5.1" level="project" />
+    <orderEntry type="library" name="Maven: com.github.jsqlparser:jsqlparser:4.3" level="project" />
+    <orderEntry type="library" name="Maven: org.mybatis:mybatis:3.5.9" level="project" />
+    <orderEntry type="library" name="Maven: org.mybatis:mybatis-spring:2.0.6" level="project" />
+    <orderEntry type="library" name="Maven: org.mybatis:mybatis-typehandlers-jsr310:1.0.2" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-jdbc:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-context:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-expression:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-logging:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: ch.qos.logback:logback-classic:1.2.11" level="project" />
+    <orderEntry type="library" name="Maven: ch.qos.logback:logback-core:1.2.11" level="project" />
+    <orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-to-slf4j:2.17.2" level="project" />
+    <orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-api:2.17.2" level="project" />
+    <orderEntry type="library" name="Maven: org.slf4j:jul-to-slf4j:1.7.30" level="project" />
+    <orderEntry type="library" name="Maven: jakarta.annotation:jakarta.annotation-api:1.3.5" level="project" />
+    <orderEntry type="library" name="Maven: org.yaml:snakeyaml:1.26" level="project" />
+    <orderEntry type="library" name="Maven: com.zaxxer:HikariCP:3.4.5" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-jdbc:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-tx:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba:druid:1.2.8" level="project" />
+    <orderEntry type="library" name="Maven: javax.annotation:javax.annotation-api:1.3.2" level="project" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-starter-auth:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-core-tool:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-core-launch:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-web:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-json:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.module:jackson-module-parameter-names:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-webmvc:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-undertow:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: io.undertow:undertow-core:2.1.7.Final" level="project" />
+    <orderEntry type="library" name="Maven: org.jboss.xnio:xnio-api:3.8.0.Final" level="project" />
+    <orderEntry type="library" name="Maven: org.wildfly.common:wildfly-common:1.5.2.Final" level="project" />
+    <orderEntry type="library" name="Maven: org.wildfly.client:wildfly-client-config:1.0.1.Final" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: org.jboss.xnio:xnio-nio:3.8.0.Final" level="project" />
+    <orderEntry type="library" name="Maven: org.jboss.threads:jboss-threads:3.1.0.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.undertow:undertow-servlet:2.1.7.Final" level="project" />
+    <orderEntry type="library" name="Maven: org.jboss.spec.javax.annotation:jboss-annotations-api_1.3_spec:2.0.1.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.undertow:undertow-websockets-jsr:2.1.7.Final" level="project" />
+    <orderEntry type="library" name="Maven: org.jboss.spec.javax.websocket:jboss-websocket-api_1.1_spec:2.0.0.Final" level="project" />
+    <orderEntry type="library" name="Maven: jakarta.servlet:jakarta.servlet-api:4.0.4" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: com.google.guava:guava:30.1.1-jre" level="project" />
+    <orderEntry type="library" name="Maven: com.google.guava:failureaccess:1.0.1" level="project" />
+    <orderEntry type="library" name="Maven: com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava" level="project" />
+    <orderEntry type="library" name="Maven: org.checkerframework:checker-qual:3.8.0" level="project" />
+    <orderEntry type="library" name="Maven: com.google.errorprone:error_prone_annotations:2.5.1" level="project" />
+    <orderEntry type="library" name="Maven: com.google.j2objc:j2objc-annotations:1.3" level="project" />
+    <orderEntry type="library" name="Maven: io.springfox:springfox-swagger2:2.10.5" level="project" />
+    <orderEntry type="library" name="Maven: io.swagger:swagger-annotations:1.5.20" level="project" />
+    <orderEntry type="library" name="Maven: io.springfox:springfox-spi:2.10.5" level="project" />
+    <orderEntry type="library" name="Maven: io.springfox:springfox-core:2.10.5" level="project" />
+    <orderEntry type="library" name="Maven: net.bytebuddy:byte-buddy:1.10.22" level="project" />
+    <orderEntry type="library" name="Maven: io.springfox:springfox-schema:2.10.5" level="project" />
+    <orderEntry type="library" name="Maven: io.springfox:springfox-swagger-common:2.10.5" level="project" />
+    <orderEntry type="library" name="Maven: io.springfox:springfox-spring-web:2.10.5" level="project" />
+    <orderEntry type="library" name="Maven: io.github.classgraph:classgraph:4.1.7" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml:classmate:1.5.1" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.plugin:spring-plugin-core:2.0.0.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.plugin:spring-plugin-metadata:2.0.0.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.mapstruct:mapstruct:1.3.1.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.swagger:swagger-models:1.6.2" level="project" />
+    <orderEntry type="library" name="Maven: io.protostuff:protostuff-core:1.6.0" level="project" />
+    <orderEntry type="library" name="Maven: io.protostuff:protostuff-api:1.6.0" level="project" />
+    <orderEntry type="library" name="Maven: io.protostuff:protostuff-runtime:1.6.0" level="project" />
+    <orderEntry type="library" name="Maven: io.protostuff:protostuff-collectionschema:1.6.0" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.module:jackson-module-jaxb-annotations:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: jakarta.xml.bind:jakarta.xml.bind-api:2.3.3" level="project" />
+    <orderEntry type="library" name="Maven: jakarta.activation:jakarta.activation-api:1.2.2" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-validation:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.glassfish:jakarta.el:3.0.3" level="project" />
+    <orderEntry type="library" name="Maven: org.hibernate.validator:hibernate-validator:6.1.7.Final" level="project" />
+    <orderEntry type="library" name="Maven: jakarta.validation:jakarta.validation-api:2.0.2" level="project" />
+    <orderEntry type="library" name="Maven: org.jboss.logging:jboss-logging:3.4.2.Final" level="project" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-starter-jwt:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-data-redis:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.data:spring-data-redis:2.3.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.data:spring-data-keyvalue:2.3.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.data:spring-data-commons:2.3.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-oxm:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-context-support:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: io.lettuce:lettuce-core:5.3.7.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-common:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-handler:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-resolver:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-buffer:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-codec:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-transport:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.projectreactor:reactor-core:3.3.17.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.reactivestreams:reactive-streams:1.0.3" level="project" />
+    <orderEntry type="library" name="Maven: io.jsonwebtoken:jjwt-impl:0.11.2" level="project" />
+    <orderEntry type="library" name="Maven: io.jsonwebtoken:jjwt-api:0.11.2" level="project" />
+    <orderEntry type="library" name="Maven: io.jsonwebtoken:jjwt-jackson:0.11.2" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-aop:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-aop:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.aspectj:aspectjweaver:1.9.6" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.retry:spring-retry:1.2.5.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-core:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-jcl:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-starter-openfeign:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-starter:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-context:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.security:spring-security-rsa:1.0.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.bouncycastle:bcpkix-jdk15on:1.59" level="project" />
+    <orderEntry type="library" name="Maven: org.bouncycastle:bcprov-jdk15on:1.59" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-openfeign-core:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-autoconfigure:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-netflix-ribbon:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-netflix-archaius:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: io.github.openfeign.form:feign-form-spring:3.8.0" level="project" />
+    <orderEntry type="library" name="Maven: io.github.openfeign.form:feign-form:3.8.0" level="project" />
+    <orderEntry type="library" name="Maven: commons-fileupload:commons-fileupload:1.3.3" level="project" />
+    <orderEntry type="library" name="Maven: commons-io:commons-io:2.6" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-web:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-beans:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-commons:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.security:spring-security-crypto:5.3.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: io.github.openfeign:feign-core:10.12" level="project" />
+    <orderEntry type="library" name="Maven: io.github.openfeign:feign-slf4j:10.12" level="project" />
+    <orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.7.30" level="project" />
+    <orderEntry type="library" name="Maven: io.github.openfeign:feign-hystrix:10.12" level="project" />
+    <orderEntry type="library" name="Maven: com.netflix.archaius:archaius-core:0.7.7" level="project" />
+    <orderEntry type="library" name="Maven: com.google.code.findbugs:jsr305:3.0.2" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: commons-configuration:commons-configuration:1.8" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: commons-lang:commons-lang:2.6" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-core:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-databind:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: com.netflix.hystrix:hystrix-core:1.5.18" level="project" />
+    <orderEntry type="library" name="Maven: io.reactivex:rxjava:1.3.8" level="project" />
+    <orderEntry type="library" name="Maven: org.hdrhistogram:HdrHistogram:2.1.9" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.springblade:blade-core-auto:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.projectlombok:lombok:1.18.22" level="project" />
+  </component>
+</module>

+ 179 - 0
blade-ops-api/blade-resource-api/blade-resource-api.iml

@@ -0,0 +1,179 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
+  <component name="FacetManager">
+    <facet type="Spring" name="Spring">
+      <configuration />
+    </facet>
+    <facet type="web" name="Web">
+      <configuration>
+        <webroots />
+      </configuration>
+    </facet>
+  </component>
+  <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
+    <output url="file://$MODULE_DIR$/target/classes" />
+    <output-test url="file://$MODULE_DIR$/target/test-classes" />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
+      <excludeFolder url="file://$MODULE_DIR$/target" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-starter-sms:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-core-tool:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-core-launch:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-web:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-json:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.module:jackson-module-parameter-names:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-webmvc:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-expression:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-undertow:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: io.undertow:undertow-core:2.1.7.Final" level="project" />
+    <orderEntry type="library" name="Maven: org.jboss.xnio:xnio-api:3.8.0.Final" level="project" />
+    <orderEntry type="library" name="Maven: org.wildfly.common:wildfly-common:1.5.2.Final" level="project" />
+    <orderEntry type="library" name="Maven: org.wildfly.client:wildfly-client-config:1.0.1.Final" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: org.jboss.xnio:xnio-nio:3.8.0.Final" level="project" />
+    <orderEntry type="library" name="Maven: org.jboss.threads:jboss-threads:3.1.0.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.undertow:undertow-servlet:2.1.7.Final" level="project" />
+    <orderEntry type="library" name="Maven: org.jboss.spec.javax.annotation:jboss-annotations-api_1.3_spec:2.0.1.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.undertow:undertow-websockets-jsr:2.1.7.Final" level="project" />
+    <orderEntry type="library" name="Maven: org.jboss.spec.javax.websocket:jboss-websocket-api_1.1_spec:2.0.0.Final" level="project" />
+    <orderEntry type="library" name="Maven: jakarta.servlet:jakarta.servlet-api:4.0.4" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: com.google.guava:guava:30.1.1-jre" level="project" />
+    <orderEntry type="library" name="Maven: com.google.guava:failureaccess:1.0.1" level="project" />
+    <orderEntry type="library" name="Maven: com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava" level="project" />
+    <orderEntry type="library" name="Maven: com.google.code.findbugs:jsr305:3.0.2" level="project" />
+    <orderEntry type="library" name="Maven: org.checkerframework:checker-qual:3.8.0" level="project" />
+    <orderEntry type="library" name="Maven: com.google.errorprone:error_prone_annotations:2.5.1" level="project" />
+    <orderEntry type="library" name="Maven: com.google.j2objc:j2objc-annotations:1.3" level="project" />
+    <orderEntry type="library" name="Maven: io.springfox:springfox-swagger2:2.10.5" level="project" />
+    <orderEntry type="library" name="Maven: io.swagger:swagger-annotations:1.5.20" level="project" />
+    <orderEntry type="library" name="Maven: io.springfox:springfox-spi:2.10.5" level="project" />
+    <orderEntry type="library" name="Maven: io.springfox:springfox-core:2.10.5" level="project" />
+    <orderEntry type="library" name="Maven: io.springfox:springfox-schema:2.10.5" level="project" />
+    <orderEntry type="library" name="Maven: io.springfox:springfox-swagger-common:2.10.5" level="project" />
+    <orderEntry type="library" name="Maven: io.springfox:springfox-spring-web:2.10.5" level="project" />
+    <orderEntry type="library" name="Maven: io.github.classgraph:classgraph:4.1.7" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml:classmate:1.5.1" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.plugin:spring-plugin-core:2.0.0.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-context:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.plugin:spring-plugin-metadata:2.0.0.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.mapstruct:mapstruct:1.3.1.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.swagger:swagger-models:1.6.2" level="project" />
+    <orderEntry type="library" name="Maven: io.protostuff:protostuff-core:1.6.0" level="project" />
+    <orderEntry type="library" name="Maven: io.protostuff:protostuff-api:1.6.0" level="project" />
+    <orderEntry type="library" name="Maven: io.protostuff:protostuff-runtime:1.6.0" level="project" />
+    <orderEntry type="library" name="Maven: io.protostuff:protostuff-collectionschema:1.6.0" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.module:jackson-module-jaxb-annotations:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: jakarta.xml.bind:jakarta.xml.bind-api:2.3.3" level="project" />
+    <orderEntry type="library" name="Maven: jakarta.activation:jakarta.activation-api:1.2.2" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-validation:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.glassfish:jakarta.el:3.0.3" level="project" />
+    <orderEntry type="library" name="Maven: org.hibernate.validator:hibernate-validator:6.1.7.Final" level="project" />
+    <orderEntry type="library" name="Maven: jakarta.validation:jakarta.validation-api:2.0.2" level="project" />
+    <orderEntry type="library" name="Maven: org.jboss.logging:jboss-logging:3.4.2.Final" level="project" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-starter-redis:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-starter-jwt:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: io.jsonwebtoken:jjwt-impl:0.11.2" level="project" />
+    <orderEntry type="library" name="Maven: io.jsonwebtoken:jjwt-api:0.11.2" level="project" />
+    <orderEntry type="library" name="Maven: io.jsonwebtoken:jjwt-jackson:0.11.2" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-data-redis:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.data:spring-data-redis:2.3.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.data:spring-data-keyvalue:2.3.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.data:spring-data-commons:2.3.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-oxm:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: io.lettuce:lettuce-core:5.3.7.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.redisson:redisson:3.11.6" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-common:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-codec:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-buffer:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-transport:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-resolver:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-resolver-dns:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-codec-dns:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-handler:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: javax.cache:cache-api:1.1.1" level="project" />
+    <orderEntry type="library" name="Maven: io.projectreactor:reactor-core:3.3.17.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.reactivestreams:reactive-streams:1.0.3" level="project" />
+    <orderEntry type="library" name="Maven: io.reactivex.rxjava2:rxjava:2.2.21" level="project" />
+    <orderEntry type="library" name="Maven: de.ruedigermoeller:fst:2.57" level="project" />
+    <orderEntry type="library" name="Maven: org.javassist:javassist:3.21.0-GA" level="project" />
+    <orderEntry type="library" name="Maven: org.objenesis:objenesis:2.6" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: net.bytebuddy:byte-buddy:1.10.22" level="project" />
+    <orderEntry type="library" name="Maven: org.jodd:jodd-bean:5.0.13" level="project" />
+    <orderEntry type="library" name="Maven: org.jodd:jodd-core:5.0.13" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-databind:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-core:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-aop:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-logging:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: ch.qos.logback:logback-classic:1.2.11" level="project" />
+    <orderEntry type="library" name="Maven: ch.qos.logback:logback-core:1.2.11" level="project" />
+    <orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-to-slf4j:2.17.2" level="project" />
+    <orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-api:2.17.2" level="project" />
+    <orderEntry type="library" name="Maven: org.slf4j:jul-to-slf4j:1.7.30" level="project" />
+    <orderEntry type="library" name="Maven: jakarta.annotation:jakarta.annotation-api:1.3.5" level="project" />
+    <orderEntry type="library" name="Maven: org.yaml:snakeyaml:1.26" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-aop:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.aspectj:aspectjweaver:1.9.6" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.retry:spring-retry:1.2.5.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-core:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-jcl:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-starter-tenant:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-starter-cache:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-cache:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-context-support:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.baomidou:dynamic-datasource-spring-boot-starter:3.3.6" level="project" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-starter-mybatis:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.baomidou:mybatis-plus:3.5.1" level="project" />
+    <orderEntry type="library" name="Maven: com.baomidou:mybatis-plus-extension:3.5.1" level="project" />
+    <orderEntry type="library" name="Maven: com.baomidou:mybatis-plus-core:3.5.1" level="project" />
+    <orderEntry type="library" name="Maven: com.baomidou:mybatis-plus-annotation:3.5.1" level="project" />
+    <orderEntry type="library" name="Maven: com.github.jsqlparser:jsqlparser:4.3" level="project" />
+    <orderEntry type="library" name="Maven: org.mybatis:mybatis:3.5.9" level="project" />
+    <orderEntry type="library" name="Maven: org.mybatis:mybatis-spring:2.0.6" level="project" />
+    <orderEntry type="library" name="Maven: org.mybatis:mybatis-typehandlers-jsr310:1.0.2" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-jdbc:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.zaxxer:HikariCP:3.4.5" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-jdbc:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-tx:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba:druid:1.2.8" level="project" />
+    <orderEntry type="library" name="Maven: javax.annotation:javax.annotation-api:1.3.2" level="project" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-starter-auth:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-starter-openfeign:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-starter:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-context:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.security:spring-security-rsa:1.0.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.bouncycastle:bcpkix-jdk15on:1.59" level="project" />
+    <orderEntry type="library" name="Maven: org.bouncycastle:bcprov-jdk15on:1.59" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-openfeign-core:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-autoconfigure:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-netflix-ribbon:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-netflix-archaius:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: io.github.openfeign.form:feign-form-spring:3.8.0" level="project" />
+    <orderEntry type="library" name="Maven: io.github.openfeign.form:feign-form:3.8.0" level="project" />
+    <orderEntry type="library" name="Maven: commons-fileupload:commons-fileupload:1.3.3" level="project" />
+    <orderEntry type="library" name="Maven: commons-io:commons-io:2.6" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-web:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-beans:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-commons:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.security:spring-security-crypto:5.3.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: io.github.openfeign:feign-core:10.12" level="project" />
+    <orderEntry type="library" name="Maven: io.github.openfeign:feign-slf4j:10.12" level="project" />
+    <orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.7.30" level="project" />
+    <orderEntry type="library" name="Maven: io.github.openfeign:feign-hystrix:10.12" level="project" />
+    <orderEntry type="library" name="Maven: com.netflix.archaius:archaius-core:0.7.7" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: commons-configuration:commons-configuration:1.8" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: commons-lang:commons-lang:2.6" level="project" />
+    <orderEntry type="library" name="Maven: com.netflix.hystrix:hystrix-core:1.5.18" level="project" />
+    <orderEntry type="library" name="Maven: io.reactivex:rxjava:1.3.8" level="project" />
+    <orderEntry type="library" name="Maven: org.hdrhistogram:HdrHistogram:2.1.9" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.springblade:blade-core-auto:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.projectlombok:lombok:1.18.22" level="project" />
+  </component>
+</module>

+ 28 - 0
blade-ops-api/blade-resource-api/pom.xml

@@ -0,0 +1,28 @@
+<?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>blade-ops-api</artifactId>
+        <groupId>org.springblade</groupId>
+        <version>2.9.1.RELEASE</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>blade-resource-api</artifactId>
+    <name>${project.artifactId}</name>
+    <version>${bladex.project.version}</version>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-starter-sms</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-starter-tenant</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>

+ 71 - 0
blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/entity/Attach.java

@@ -0,0 +1,71 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.resource.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springblade.core.tenant.mp.TenantEntity;
+
+/**
+ * 附件表实体类
+ *
+ * @author Chill
+ */
+@Data
+@TableName("blade_attach")
+@EqualsAndHashCode(callSuper = true)
+@ApiModel(value = "Attach对象", description = "附件表")
+public class Attach extends TenantEntity {
+
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * 附件地址
+	 */
+	@ApiModelProperty(value = "附件地址")
+	private String link;
+	/**
+	 * 附件域名
+	 */
+	@ApiModelProperty(value = "附件域名")
+	private String domainUrl;
+	/**
+	 * 附件名称
+	 */
+	@ApiModelProperty(value = "附件名称")
+	private String name;
+	/**
+	 * 附件原名
+	 */
+	@ApiModelProperty(value = "附件原名")
+	private String originalName;
+	/**
+	 * 附件拓展名
+	 */
+	@ApiModelProperty(value = "附件拓展名")
+	private String extension;
+	/**
+	 * 附件大小
+	 */
+	@ApiModelProperty(value = "附件大小")
+	private Long attachSize;
+
+
+}

+ 87 - 0
blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/entity/Oss.java

@@ -0,0 +1,87 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.resource.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springblade.core.tenant.mp.TenantEntity;
+
+/**
+ * 实体类
+ *
+ * @author BladeX
+ */
+@Data
+@TableName("blade_oss")
+@EqualsAndHashCode(callSuper = true)
+@ApiModel(value = "Oss对象", description = "Oss对象")
+public class Oss extends TenantEntity {
+
+	private static final long serialVersionUID = 1L;
+	/**
+	 * 所属分类
+	 */
+	@ApiModelProperty(value = "所属分类")
+	private Integer category;
+
+	/**
+	 * 资源编号
+	 */
+	@ApiModelProperty(value = "资源编号")
+	private String ossCode;
+
+	/**
+	 * oss地址
+	 */
+	@ApiModelProperty(value = "资源地址")
+	private String endpoint;
+	/**
+	 * accessKey
+	 */
+	@ApiModelProperty(value = "accessKey")
+	private String accessKey;
+	/**
+	 * secretKey
+	 */
+	@ApiModelProperty(value = "secretKey")
+	private String secretKey;
+	/**
+	 * 空间名
+	 */
+	@ApiModelProperty(value = "空间名")
+	private String bucketName;
+	/**
+	 * 应用ID TencentCOS需要
+	 */
+	@ApiModelProperty(value = "应用ID")
+	private String appId;
+	/**
+	 * 地域简称 TencentCOS需要
+	 */
+	@ApiModelProperty(value = "地域简称")
+	private String region;
+	/**
+	 * 备注
+	 */
+	@ApiModelProperty(value = "备注")
+	private String remark;
+
+
+}

+ 82 - 0
blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/entity/Sms.java

@@ -0,0 +1,82 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.resource.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springblade.core.tenant.mp.TenantEntity;
+
+/**
+ * 短信配置表实体类
+ *
+ * @author BladeX
+ */
+@Data
+@TableName("blade_sms")
+@EqualsAndHashCode(callSuper = true)
+@ApiModel(value = "Sms对象", description = "短信配置表")
+public class Sms extends TenantEntity {
+
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * 资源编号
+	 */
+	@ApiModelProperty(value = "资源编号")
+	private String smsCode;
+
+	/**
+	 * 模板ID
+	 */
+	@ApiModelProperty(value = "模板ID")
+	private String templateId;
+	/**
+	 * 分类
+	 */
+	@ApiModelProperty(value = "分类")
+	private Integer category;
+	/**
+	 * accessKey
+	 */
+	@ApiModelProperty(value = "accessKey")
+	private String accessKey;
+	/**
+	 * secretKey
+	 */
+	@ApiModelProperty(value = "secretKey")
+	private String secretKey;
+	/**
+	 * regionId
+	 */
+	@ApiModelProperty(value = "regionId")
+	private String regionId;
+	/**
+	 * 短信签名
+	 */
+	@ApiModelProperty(value = "短信签名")
+	private String signName;
+	/**
+	 * 备注
+	 */
+	@ApiModelProperty(value = "备注")
+	private String remark;
+
+
+}

+ 62 - 0
blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/enums/SmsCodeEnum.java

@@ -0,0 +1,62 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.resource.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import org.springblade.core.tool.utils.StringPool;
+
+/**
+ * Sms资源编码枚举类
+ *
+ * @author Chill
+ * @apiNote 该枚举类对应短信配置模块的资源编码,可根据业务需求自行拓展
+ */
+@Getter
+@AllArgsConstructor
+public enum SmsCodeEnum {
+
+	/**
+	 * 默认编号
+	 */
+	DEFAULT(StringPool.EMPTY, 1),
+
+	/**
+	 * 验证码编号
+	 */
+	VALIDATE("validate", 2),
+
+	/**
+	 * 通知公告编号
+	 */
+	NOTICE("notice", 3),
+
+	/**
+	 * 下单通知编号
+	 */
+	ORDER("order", 4),
+
+	/**
+	 * 会议通知编号
+	 */
+	MEETING("meeting", 5),
+	;
+
+	final String name;
+	final int category;
+
+}

+ 74 - 0
blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/feign/ISmsClient.java

@@ -0,0 +1,74 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.resource.feign;
+
+import org.springblade.core.launch.constant.AppConstant;
+import org.springblade.core.sms.model.SmsResponse;
+import org.springblade.core.tool.api.R;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+
+/**
+ * ISmsClient
+ *
+ * @author Chill
+ */
+@FeignClient(
+	value = AppConstant.APPLICATION_RESOURCE_NAME,
+	fallback = ISmsClientFallback.class
+)
+public interface ISmsClient {
+	String API_PREFIX = "/client";
+	String SEND_MESSAGE = API_PREFIX + "/send-message";
+	String SEND_VALIDATE = API_PREFIX + "/send-validate";
+	String VALIDATE_MESSAGE = API_PREFIX + "/validate-message";
+
+	/**
+	 * 通用短信发送
+	 *
+	 * @param code   资源编号
+	 * @param params 模板参数
+	 * @param phones 手机号集合
+	 * @return R
+	 */
+	@PostMapping(SEND_MESSAGE)
+	R<SmsResponse> sendMessage(@RequestParam("code") String code, @RequestParam("params") String params, @RequestParam("phones") String phones);
+
+	/**
+	 * 短信验证码发送
+	 *
+	 * @param code  资源编号
+	 * @param phone 手机号
+	 * @return R
+	 */
+	@PostMapping(SEND_VALIDATE)
+	R sendValidate(@RequestParam("code") String code, @RequestParam("phone") String phone);
+
+	/**
+	 * 校验短信
+	 *
+	 * @param code  资源编号
+	 * @param id    校验id
+	 * @param value 校验值
+	 * @param phone 手机号
+	 * @return R
+	 */
+	@PostMapping(VALIDATE_MESSAGE)
+	R validateMessage(@RequestParam("code") String code, @RequestParam("id") String id, @RequestParam("value") String value, @RequestParam("phone") String phone);
+
+}

+ 44 - 0
blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/feign/ISmsClientFallback.java

@@ -0,0 +1,44 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.resource.feign;
+
+import org.springblade.core.tool.api.R;
+import org.springframework.stereotype.Component;
+
+/**
+ * 流程远程调用失败处理类
+ *
+ * @author Chill
+ */
+@Component
+public class ISmsClientFallback implements ISmsClient {
+	@Override
+	public R sendMessage(String code, String params, String phones) {
+		return R.fail("远程调用失败");
+	}
+
+	@Override
+	public R sendValidate(String code, String phone) {
+		return R.fail("远程调用失败");
+	}
+
+	@Override
+	public R validateMessage(String code, String id, String value, String phone) {
+		return R.fail("远程调用失败");
+	}
+
+}

+ 113 - 0
blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/utils/SmsUtil.java

@@ -0,0 +1,113 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.resource.utils;
+
+import org.springblade.core.sms.model.SmsCode;
+import org.springblade.core.sms.model.SmsResponse;
+import org.springblade.core.tool.api.R;
+import org.springblade.core.tool.jackson.JsonUtil;
+import org.springblade.core.tool.utils.RandomType;
+import org.springblade.core.tool.utils.SpringUtil;
+import org.springblade.core.tool.utils.StringUtil;
+import org.springblade.resource.feign.ISmsClient;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 短信服务工具类
+ *
+ * @author Chill
+ */
+public class SmsUtil {
+
+	public static final String PARAM_KEY = "code";
+	public static final String SEND_SUCCESS = "短信发送成功";
+	public static final String SEND_FAIL = "短信发送失败";
+	public static final String VALIDATE_SUCCESS = "短信校验成功";
+	public static final String VALIDATE_FAIL = "短信校验失败";
+
+	private static ISmsClient smsClient;
+
+	/**
+	 * 获取短信服务构建类
+	 *
+	 * @return SmsBuilder
+	 */
+	public static ISmsClient getSmsClient() {
+		if (smsClient == null) {
+			smsClient = SpringUtil.getBean(ISmsClient.class);
+		}
+		return smsClient;
+	}
+
+	/**
+	 * 获取短信验证码参数
+	 *
+	 * @return 验证码参数
+	 */
+	public static Map<String, String> getValidateParams() {
+		Map<String, String> params = new HashMap<>(1);
+		params.put(PARAM_KEY, StringUtil.random(6, RandomType.INT));
+		return params;
+	}
+
+	/**
+	 * 发送短信
+	 *
+	 * @param code   资源编号
+	 * @param params 模板参数
+	 * @param phones 手机号集合
+	 * @return 发送结果
+	 */
+	public static SmsResponse sendMessage(String code, Map<String, String> params, String phones) {
+		R<SmsResponse> result = getSmsClient().sendMessage(code, JsonUtil.toJson(params), phones);
+		return result.getData();
+	}
+
+	/**
+	 * 发送验证码
+	 *
+	 * @param code  资源编号
+	 * @param phone 手机号
+	 * @return 发送结果
+	 */
+	public static SmsCode sendValidate(String code, String phone) {
+		SmsCode smsCode = new SmsCode();
+		R result = getSmsClient().sendValidate(code, phone);
+		if (result.isSuccess()) {
+			smsCode = JsonUtil.parse(JsonUtil.toJson(result.getData()), SmsCode.class);
+		} else {
+			smsCode.setSuccess(Boolean.FALSE);
+		}
+		return smsCode;
+	}
+
+	/**
+	 * 校验短信
+	 *
+	 * @param code  资源编号
+	 * @param id    校验id
+	 * @param value 校验值
+	 * @return 发送结果
+	 */
+	public static boolean validateMessage(String code, String id, String value, String phone) {
+		R result = getSmsClient().validateMessage(code, id, value, phone);
+		return result.isSuccess();
+	}
+
+}

+ 35 - 0
blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/vo/AttachVO.java

@@ -0,0 +1,35 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.resource.vo;
+
+import io.swagger.annotations.ApiModel;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springblade.resource.entity.Attach;
+
+/**
+ * 附件表视图实体类
+ *
+ * @author Chill
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ApiModel(value = "AttachVO对象", description = "附件表")
+public class AttachVO extends Attach {
+	private static final long serialVersionUID = 1L;
+
+}

+ 29 - 0
blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/vo/OssVO.java

@@ -0,0 +1,29 @@
+package org.springblade.resource.vo;
+
+import io.swagger.annotations.ApiModel;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springblade.resource.entity.Oss;
+
+/**
+ * OssVO
+ *
+ * @author Chill
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ApiModel(value = "OssVO对象", description = "对象存储表")
+public class OssVO extends Oss {
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * 分类名
+	 */
+	private String categoryName;
+
+	/**
+	 * 是否启用
+	 */
+	private String statusName;
+
+}

+ 45 - 0
blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/vo/SmsVO.java

@@ -0,0 +1,45 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.resource.vo;
+
+import io.swagger.annotations.ApiModel;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springblade.resource.entity.Sms;
+
+/**
+ * 短信配置表视图实体类
+ *
+ * @author BladeX
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ApiModel(value = "SmsVO对象", description = "短信配置表")
+public class SmsVO extends Sms {
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * 分类名
+	 */
+	private String categoryName;
+
+	/**
+	 * 是否启用
+	 */
+	private String statusName;
+
+}

+ 52 - 0
blade-ops-api/pom.xml

@@ -0,0 +1,52 @@
+<?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>BladeX</artifactId>
+        <groupId>org.springblade</groupId>
+        <version>2.9.1.RELEASE</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>blade-ops-api</artifactId>
+    <name>${project.artifactId}</name>
+    <version>2.9.1.RELEASE</version>
+    <packaging>pom</packaging>
+    <description>BladeX 微服务API集合</description>
+
+    <modules>
+        <module>blade-flow-api</module>
+        <module>blade-resource-api</module>
+    </modules>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-starter-mybatis</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-openfeign</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-core-auto</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <configuration>
+                    <skip>true</skip>
+                    <finalName>${project.name}</finalName>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 15 - 0
blade-ops/blade-admin/Dockerfile

@@ -0,0 +1,15 @@
+FROM bladex/alpine-java:openjdk8-openj9_cn_slim
+
+MAINTAINER bladejava@qq.com
+
+RUN mkdir -p /blade/admin
+
+WORKDIR /blade/admin
+
+EXPOSE 7002
+
+ADD ./target/blade-admin.jar ./app.jar
+
+ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "app.jar"]
+
+CMD ["--spring.profiles.active=test"]

+ 21 - 0
blade-ops/blade-admin/README.md

@@ -0,0 +1,21 @@
+## SDK下载
+
+#### Java SDK 下载
+    下载SDK: https://open-doc.dingtalk.com/microapp/faquestions/vzbp02
+    
+## 配置项
+
+#### bootstrap.yml
+
+```
+# 监控的相关配置
+monitor:
+  ding-talk:
+    enabled: false
+    # 用于自定义域名,默认会自动填充为 http://ip:port
+    link: http://localhost:${server.port}
+    # 钉钉配置的令牌
+    access-token: xxx
+    # 如果采用密钥形式,需要添加,否则需要去掉该参数
+    secret: xxx
+```

+ 172 - 0
blade-ops/blade-admin/blade-admin.iml

@@ -0,0 +1,172 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
+  <component name="FacetManager">
+    <facet type="Spring" name="Spring">
+      <configuration />
+    </facet>
+  </component>
+  <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
+    <output url="file://$MODULE_DIR$/target/classes" />
+    <output-test url="file://$MODULE_DIR$/target/test-classes" />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
+      <excludeFolder url="file://$MODULE_DIR$/target" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="module" module-name="blade-common" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-starter-ribbon:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-core-launch:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-aop:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-context:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-logging:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: ch.qos.logback:logback-classic:1.2.11" level="project" />
+    <orderEntry type="library" name="Maven: ch.qos.logback:logback-core:1.2.11" level="project" />
+    <orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-to-slf4j:2.17.2" level="project" />
+    <orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-api:2.17.2" level="project" />
+    <orderEntry type="library" name="Maven: org.slf4j:jul-to-slf4j:1.7.30" level="project" />
+    <orderEntry type="library" name="Maven: jakarta.annotation:jakarta.annotation-api:1.3.5" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-aop:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-beans:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.aspectj:aspectjweaver:1.9.6" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.retry:spring-retry:1.2.5.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-core:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-jcl:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-starter-prometheus:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-webflux:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-json:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.module:jackson-module-parameter-names:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-reactor-netty:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: io.projectreactor.netty:reactor-netty:0.9.20.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-codec-http:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-common:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-buffer:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-transport:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-codec:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-codec-http2:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-handler:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-resolver:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-handler-proxy:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-codec-socks:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-transport-native-epoll:linux-x86_64:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-transport-native-unix-common:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-web:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-webflux:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: io.projectreactor:reactor-core:3.3.17.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.synchronoss.cloud:nio-multipart-parser:1.1.0" level="project" />
+    <orderEntry type="library" name="Maven: org.synchronoss.cloud:nio-stream-storage:1.1.3" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-commons:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.security:spring-security-crypto:5.3.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-starter-netflix-hystrix:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-starter:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.security:spring-security-rsa:1.0.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.bouncycastle:bcpkix-jdk15on:1.59" level="project" />
+    <orderEntry type="library" name="Maven: org.bouncycastle:bcprov-jdk15on:1.59" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-netflix-hystrix:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-autoconfigure:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-netflix-ribbon:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-netflix-archaius:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-starter-netflix-archaius:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.netflix.archaius:archaius-core:0.7.7" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: com.google.code.findbugs:jsr305:3.0.2" level="project" />
+    <orderEntry type="library" name="Maven: commons-configuration:commons-configuration:1.8" level="project" />
+    <orderEntry type="library" name="Maven: commons-lang:commons-lang:2.6" level="project" />
+    <orderEntry type="library" name="Maven: com.netflix.hystrix:hystrix-core:1.5.18" level="project" />
+    <orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.7.30" level="project" />
+    <orderEntry type="library" name="Maven: io.reactivex:rxjava:1.3.8" level="project" />
+    <orderEntry type="library" name="Maven: org.hdrhistogram:HdrHistogram:2.1.9" level="project" />
+    <orderEntry type="library" name="Maven: com.netflix.hystrix:hystrix-serialization:1.5.18" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: com.fasterxml.jackson.module:jackson-module-afterburner:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: com.netflix.hystrix:hystrix-metrics-event-stream:1.5.18" level="project" />
+    <orderEntry type="library" name="Maven: com.netflix.hystrix:hystrix-javanica:1.5.18" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: org.apache.commons:commons-lang3:3.10" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: org.ow2.asm:asm:5.0.4" level="project" />
+    <orderEntry type="library" name="Maven: com.google.guava:guava:30.1.1-jre" level="project" />
+    <orderEntry type="library" name="Maven: com.google.guava:failureaccess:1.0.1" level="project" />
+    <orderEntry type="library" name="Maven: com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava" level="project" />
+    <orderEntry type="library" name="Maven: org.checkerframework:checker-qual:3.8.0" level="project" />
+    <orderEntry type="library" name="Maven: com.google.errorprone:error_prone_annotations:2.5.1" level="project" />
+    <orderEntry type="library" name="Maven: com.google.j2objc:j2objc-annotations:1.3" level="project" />
+    <orderEntry type="library" name="Maven: io.reactivex:rxjava-reactive-streams:1.2.1" level="project" />
+    <orderEntry type="library" name="Maven: org.reactivestreams:reactive-streams:1.0.3" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-discovery:2.2.7.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.cloud:spring-cloud-alibaba-commons:2.2.7.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.spring:spring-context-support:1.0.10" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-context:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-starter-netflix-ribbon:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.netflix.ribbon:ribbon:2.3.0" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: com.netflix.ribbon:ribbon-transport:2.3.0" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: io.reactivex:rxnetty-contexts:0.4.9" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: io.reactivex:rxnetty-servo:0.4.9" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: javax.inject:javax.inject:1" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: io.reactivex:rxnetty:0.4.9" level="project" />
+    <orderEntry type="library" name="Maven: com.netflix.ribbon:ribbon-core:2.3.0" level="project" />
+    <orderEntry type="library" name="Maven: com.netflix.ribbon:ribbon-httpclient:2.3.0" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: commons-collections:commons-collections:3.2.2" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: com.sun.jersey:jersey-client:1.19.1" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: com.sun.jersey:jersey-core:1.19.1" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: javax.ws.rs:jsr311-api:1.1.1" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: com.sun.jersey.contribs:jersey-apache-client4:1.19.1" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: com.netflix.servo:servo-core:0.12.21" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: com.netflix.netflix-commons:netflix-commons-util:0.3.0" level="project" />
+    <orderEntry type="library" name="Maven: com.netflix.ribbon:ribbon-loadbalancer:2.3.0" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: com.netflix.netflix-commons:netflix-statistics:0.1.1" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-config:2.2.7.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.nacos:nacos-client:2.0.4" level="project" />
+    <orderEntry type="library" name="Maven: commons-codec:commons-codec:1.14" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-core:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-databind:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: org.apache.httpcomponents:httpasyncclient:4.1.4" level="project" />
+    <orderEntry type="library" name="Maven: org.apache.httpcomponents:httpcore:4.4.14" level="project" />
+    <orderEntry type="library" name="Maven: org.apache.httpcomponents:httpcore-nio:4.4.14" level="project" />
+    <orderEntry type="library" name="Maven: org.apache.httpcomponents:httpclient:4.5.13" level="project" />
+    <orderEntry type="library" name="Maven: org.reflections:reflections:0.9.11" level="project" />
+    <orderEntry type="library" name="Maven: org.javassist:javassist:3.21.0-GA" level="project" />
+    <orderEntry type="library" name="Maven: io.prometheus:simpleclient:0.5.0" level="project" />
+    <orderEntry type="library" name="Maven: org.yaml:snakeyaml:1.26" level="project" />
+    <orderEntry type="library" name="Maven: de.codecentric:spring-boot-admin-starter-server:2.3.1" level="project" />
+    <orderEntry type="library" name="Maven: de.codecentric:spring-boot-admin-server:2.3.1" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-thymeleaf:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.thymeleaf:thymeleaf-spring5:3.0.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.thymeleaf:thymeleaf:3.0.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.attoparser:attoparser:2.0.5.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.unbescape:unbescape:1.1.6.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.thymeleaf.extras:thymeleaf-extras-java8time:3.0.4.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: io.projectreactor.addons:reactor-extra:3.3.6.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: de.codecentric:spring-boot-admin-server-ui:2.3.1" level="project" />
+    <orderEntry type="library" name="Maven: de.codecentric:spring-boot-admin-server-cloud:2.3.1" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-starter-security:2.2.5.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-actuator:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-actuator-autoconfigure:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-actuator:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-security:2.2.5.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-security:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.security:spring-security-config:5.3.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.security:spring-security-core:5.3.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.security:spring-security-web:5.3.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-expression:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: javax.activation:javax.activation-api:1.2.0" level="project" />
+    <orderEntry type="library" name="Maven: javax.xml.bind:jaxb-api:2.3.1" level="project" />
+    <orderEntry type="library" name="Maven: org.glassfish.jaxb:jaxb-runtime:2.3.4" level="project" />
+    <orderEntry type="library" name="Maven: jakarta.xml.bind:jakarta.xml.bind-api:2.3.3" level="project" />
+    <orderEntry type="library" name="Maven: org.glassfish.jaxb:txw2:2.3.4" level="project" />
+    <orderEntry type="library" name="Maven: com.sun.istack:istack-commons-runtime:3.0.12" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: com.sun.activation:jakarta.activation:1.2.2" level="project" />
+    <orderEntry type="library" name="Maven: com.taobao:taobao-sdk:20201116" level="project" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-starter-metrics:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: io.micrometer:micrometer-core:1.6.3" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: org.latencyutils:LatencyUtils:2.0.3" level="project" />
+    <orderEntry type="library" name="Maven: io.micrometer:micrometer-registry-prometheus:1.6.3" level="project" />
+    <orderEntry type="library" name="Maven: io.prometheus:simpleclient_common:0.9.0" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.csp:sentinel-core:1.8.3" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba:druid:1.2.8" level="project" />
+    <orderEntry type="library" name="Maven: javax.annotation:javax.annotation-api:1.3.2" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.projectlombok:lombok:1.18.22" level="project" />
+  </component>
+</module>

+ 125 - 0
blade-ops/blade-admin/pom.xml

@@ -0,0 +1,125 @@
+<?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>blade-ops</artifactId>
+        <groupId>org.springblade</groupId>
+        <version>2.9.1.RELEASE</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>blade-admin</artifactId>
+    <name>${project.artifactId}</name>
+    <version>${bladex.project.version}</version>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <!--Blade-->
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-common</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.springblade</groupId>
+                    <artifactId>blade-core-launch</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-core-launch</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-starter-web</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-starter-undertow</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-starter-prometheus</artifactId>
+        </dependency>
+        <!-- Hystrix -->
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>commons-logging</groupId>
+                    <artifactId>commons-logging</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <!-- Nacos -->
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>com.alibaba.nacos</groupId>
+                    <artifactId>nacos-client</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>com.alibaba.nacos</groupId>
+                    <artifactId>nacos-client</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba.nacos</groupId>
+            <artifactId>nacos-client</artifactId>
+        </dependency>
+        <!--Admin-Server-->
+        <dependency>
+            <groupId>de.codecentric</groupId>
+            <artifactId>spring-boot-admin-starter-server</artifactId>
+        </dependency>
+        <!--Security-->
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-security</artifactId>
+        </dependency>
+        <!--Taobao-Sdk-->
+        <dependency>
+            <groupId>com.taobao</groupId>
+            <artifactId>taobao-sdk</artifactId>
+            <version>20201116</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>com.spotify</groupId>
+                <artifactId>dockerfile-maven-plugin</artifactId>
+                <configuration>
+                    <username>${docker.username}</username>
+                    <password>${docker.password}</password>
+                    <repository>${docker.registry.url}/${docker.namespace}/${project.artifactId}</repository>
+                    <tag>${project.version}</tag>
+                    <useMavenSettingsForAuth>true</useMavenSettingsForAuth>
+                    <buildArgs>
+                        <JAR_FILE>target/${project.build.finalName}.jar</JAR_FILE>
+                    </buildArgs>
+                    <skip>false</skip>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-antrun-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 37 - 0
blade-ops/blade-admin/src/main/java/org/springblade/admin/AdminApplication.java

@@ -0,0 +1,37 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.admin;
+
+import de.codecentric.boot.admin.server.config.EnableAdminServer;
+import org.springblade.core.launch.BladeApplication;
+import org.springblade.core.launch.constant.AppConstant;
+import org.springframework.cloud.client.SpringCloudApplication;
+
+/**
+ * admin启动器
+ *
+ * @author Chill
+ */
+@EnableAdminServer
+@SpringCloudApplication
+public class AdminApplication {
+
+	public static void main(String[] args) {
+		BladeApplication.run(AppConstant.APPLICATION_ADMIN_NAME, AdminApplication.class, args);
+	}
+
+}

+ 32 - 0
blade-ops/blade-admin/src/main/java/org/springblade/admin/config/AdminConfiguration.java

@@ -0,0 +1,32 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.admin.config;
+
+import org.springblade.admin.dingtalk.MonitorProperties;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * 启动器
+ *
+ * @author Chill
+ */
+@Configuration
+@EnableConfigurationProperties(MonitorProperties.class)
+public class AdminConfiguration {
+
+}

+ 52 - 0
blade-ops/blade-admin/src/main/java/org/springblade/admin/config/DingTalkConfiguration.java

@@ -0,0 +1,52 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.admin.config;
+
+import de.codecentric.boot.admin.server.domain.entities.InstanceRepository;
+import org.springblade.admin.dingtalk.DingTalkNotifier;
+import org.springblade.admin.dingtalk.DingTalkService;
+import org.springblade.admin.dingtalk.MonitorProperties;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.env.Environment;
+import org.springframework.web.reactive.function.client.WebClient;
+
+/**
+ * 钉钉自动配置
+ *
+ * @author L.cm
+ */
+@Configuration
+@ConditionalOnProperty(value = "monitor.ding-talk.enabled", havingValue = "true")
+public class DingTalkConfiguration {
+
+	@Bean
+	public DingTalkService dingTalkService(MonitorProperties properties,
+										   WebClient.Builder builder) {
+		return new DingTalkService(properties, builder.build());
+	}
+
+	@Bean
+	public DingTalkNotifier dingTalkNotifier(MonitorProperties properties,
+											 DingTalkService dingTalkService,
+											 InstanceRepository repository,
+											 Environment environment) {
+		return new DingTalkNotifier(dingTalkService, properties, environment, repository);
+	}
+
+}

+ 71 - 0
blade-ops/blade-admin/src/main/java/org/springblade/admin/config/SecurityConfiguration.java

@@ -0,0 +1,71 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.admin.config;
+
+import de.codecentric.boot.admin.server.config.AdminServerProperties;
+import org.springblade.admin.security.InternalAuthorizationManager;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
+import org.springframework.security.config.web.server.ServerHttpSecurity;
+import org.springframework.security.web.server.SecurityWebFilterChain;
+import org.springframework.security.web.server.authentication.RedirectServerAuthenticationSuccessHandler;
+
+import java.net.URI;
+
+/**
+ * 监控安全配置
+ *
+ * @author L.cm
+ */
+@EnableWebFluxSecurity
+@Configuration(proxyBeanMethods = false)
+public class SecurityConfiguration {
+	private final String contextPath;
+
+	public SecurityConfiguration(AdminServerProperties adminServerProperties) {
+		this.contextPath = adminServerProperties.getContextPath();
+	}
+
+	@Bean
+	public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
+		// @formatter:off
+		RedirectServerAuthenticationSuccessHandler successHandler = new RedirectServerAuthenticationSuccessHandler();
+		successHandler.setLocation(URI.create(contextPath + "/"));
+		return http.headers().frameOptions().disable().and()
+			.authorizeExchange()
+			// 放开静态文件和登陆
+			.pathMatchers(
+				contextPath + "/assets/**"
+				, contextPath + "/login"
+				, contextPath + "/v1/agent/**"
+				, contextPath + "/v1/catalog/**"
+				, contextPath + "/v1/health/**"
+			).permitAll()
+			// 内网可访问 actuator
+			.pathMatchers(contextPath + "/actuator", contextPath + "/actuator/**").access(new InternalAuthorizationManager())
+			.anyExchange().authenticated().and()
+			.formLogin().loginPage(contextPath + "/login")
+			.authenticationSuccessHandler(successHandler).and()
+			.logout().logoutUrl(contextPath + "/logout").and()
+			.httpBasic().disable()
+			.csrf().disable()
+			.build();
+		// @formatter:on
+	}
+
+}

+ 105 - 0
blade-ops/blade-admin/src/main/java/org/springblade/admin/dingtalk/DingTalkNotifier.java

@@ -0,0 +1,105 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.admin.dingtalk;
+
+import de.codecentric.boot.admin.server.domain.entities.Instance;
+import de.codecentric.boot.admin.server.domain.entities.InstanceRepository;
+import de.codecentric.boot.admin.server.domain.events.InstanceEvent;
+import de.codecentric.boot.admin.server.domain.events.InstanceStatusChangedEvent;
+import de.codecentric.boot.admin.server.domain.values.Registration;
+import de.codecentric.boot.admin.server.domain.values.StatusInfo;
+import de.codecentric.boot.admin.server.notify.AbstractEventNotifier;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.core.env.Environment;
+import org.springframework.lang.NonNull;
+import reactor.core.publisher.Mono;
+
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+
+/**
+ * 服务上下线告警
+ *
+ * <p>
+ * 注意:AbstractStatusChangeNotifier 这个事件有毛病
+ * </p>
+ *
+ * @author L.cm
+ */
+@Slf4j
+public class DingTalkNotifier extends AbstractEventNotifier {
+	private final DingTalkService dingTalkService;
+	private final MonitorProperties properties;
+	private final Environment environment;
+	public static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+
+	public DingTalkNotifier(DingTalkService dingTalkService, MonitorProperties properties,
+							Environment environment, InstanceRepository repository) {
+		super(repository);
+		this.dingTalkService = dingTalkService;
+		this.properties = properties;
+		this.environment = environment;
+	}
+
+	@NonNull
+	@Override
+	protected Mono<Void> doNotify(@NonNull InstanceEvent event, @NonNull Instance instance) {
+		if (event instanceof InstanceStatusChangedEvent) {
+			// 构造请求结构
+			return createAndPushMsg(event, instance);
+		}
+		return Mono.empty();
+	}
+
+	private Mono<Void> createAndPushMsg(InstanceEvent event, Instance instance) {
+		Registration registration = instance.getRegistration();
+		// 服务名
+		String appName = registration.getName();
+		// 服务地址
+		String serviceUrl = registration.getServiceUrl();
+		StatusInfo status = instance.getStatusInfo();
+		// 时间
+		LocalDateTime localDateTime = LocalDateTime.ofInstant(event.getTimestamp(), ZoneId.systemDefault());
+		MonitorProperties.DingTalk dingTalk = properties.getDingTalk();
+		String title = dingTalk.getService().getTitle();
+
+		String message = "## **" + title + "**\n" +
+			"#### **【服务】** " + appName + "\n" +
+			"#### **【环境】** " + environment.getActiveProfiles()[0] + "\n" +
+			"#### **【地址】** " + serviceUrl + "\n" +
+			"#### **【状态】** " + statusCn(status) + "\n" +
+			"#### **【时间】** " + DATETIME_FORMATTER.format(localDateTime) + "\n" +
+			"#### **【详情】** " + dingTalk.getLink() + "\n";
+
+		return dingTalkService.pushMsg(title, message);
+	}
+
+	private String statusCn(StatusInfo status) {
+		if (status.isUp()) {
+			return "应用上线(IS UP)";
+		} else if (status.isDown()) {
+			return "应用宕机(IS DOWN)";
+		} else if (status.isOffline()) {
+			return "应用掉线(IS OFFLINE)";
+		} else if (status.isUnknown()) {
+			return "未知状态(UNKNOWN)";
+		} else {
+			return "异常状态";
+		}
+	}
+}

+ 110 - 0
blade-ops/blade-admin/src/main/java/org/springblade/admin/dingtalk/DingTalkService.java

@@ -0,0 +1,110 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.admin.dingtalk;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.MediaType;
+import org.springframework.util.Base64Utils;
+import org.springframework.util.StringUtils;
+import org.springframework.web.reactive.function.BodyInserters;
+import org.springframework.web.reactive.function.client.WebClient;
+import org.springframework.web.util.UriUtils;
+import reactor.core.publisher.Mono;
+
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 钉钉 服务
+ *
+ * @author L.cm
+ */
+@Slf4j
+@RequiredArgsConstructor
+public class DingTalkService {
+	private static final String DING_TALK_ROBOT_URL = "https://oapi.dingtalk.com/robot/send?access_token=";
+	private final MonitorProperties properties;
+	private final WebClient webClient;
+
+	/**
+	 * 发送消息
+	 *
+	 * @param title title
+	 * @param text  消息
+	 */
+	public Mono<Void> pushMsg(String title, String text) {
+		log.info("钉钉消息:[创建消息体]title:{}, text:{}", title, text);
+
+		HashMap<String, String> params = new HashMap<>(2);
+		params.put("title", title);
+		params.put("text", text);
+
+		Map<String, Object> body = new HashMap<>(2);
+		body.put("msgtype", "markdown");
+		body.put("markdown", params);
+		log.info("创建消息体 json:{}", body);
+
+		MonitorProperties.DingTalk dingTalk = properties.getDingTalk();
+		String accessToken = dingTalk.getAccessToken();
+		if (StringUtils.isEmpty(accessToken)) {
+			log.error("DingTalk alert config accessToken ${monitor.ding-talk.access-token} is blank.");
+			return Mono.empty();
+		}
+
+		String urlString = DING_TALK_ROBOT_URL + dingTalk.getAccessToken();
+		// 有私钥要签名
+		String secret = dingTalk.getSecret();
+		if (StringUtils.hasText(secret)) {
+			long timestamp = System.currentTimeMillis();
+			urlString += String.format("&timestamp=%s&sign=%s", timestamp, getSign(secret, timestamp));
+		}
+		return webClient.post()
+			.uri(URI.create(urlString))
+			.contentType(MediaType.APPLICATION_JSON)
+			.body(BodyInserters.fromValue(body))
+			.retrieve()
+			.bodyToMono(String.class)
+			.doOnSuccess((result) -> log.info("钉钉消息:[消息返回]result:{}", result))
+			.then();
+	}
+
+	private static String getSign(String secret, long timestamp) {
+		String stringToSign = timestamp + "\n" + secret;
+		byte[] hmacSha256Bytes = digestHmac(stringToSign, secret);
+		return UriUtils.encode(Base64Utils.encodeToString(hmacSha256Bytes), StandardCharsets.UTF_8);
+	}
+
+	public static byte[] digestHmac(String data, String key) {
+		SecretKey secretKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
+		try {
+			Mac mac = Mac.getInstance(secretKey.getAlgorithm());
+			mac.init(secretKey);
+			return mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
+		} catch (NoSuchAlgorithmException | InvalidKeyException e) {
+			throw new RuntimeException(e.getMessage());
+		}
+	}
+
+}

+ 67 - 0
blade-ops/blade-admin/src/main/java/org/springblade/admin/dingtalk/MonitorProperties.java

@@ -0,0 +1,67 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.admin.dingtalk;
+
+
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
+
+/**
+ * 监控配置
+ *
+ * @author L.cm
+ */
+@Getter
+@Setter
+@RefreshScope
+@ConfigurationProperties("monitor")
+public class MonitorProperties {
+	private DingTalk dingTalk = new DingTalk();
+
+	@Getter
+	@Setter
+	public static class DingTalk {
+		/**
+		 * 启用钉钉告警,默认为 true
+		 */
+		private boolean enabled = false;
+		/**
+		 * 钉钉机器人 token
+		 */
+		private String accessToken;
+		/**
+		 * 签名:如果有 secret 则进行签名,兼容老接口
+		 */
+		private String secret;
+		/**
+		 * 地址配置
+		 */
+		private String link;
+		private Service service = new Service();
+	}
+
+	@Getter
+	@Setter
+	public static class Service {
+		/**
+		 * 服务 状态 title
+		 */
+		private String title = "服务状态通知";
+	}
+}

+ 75 - 0
blade-ops/blade-admin/src/main/java/org/springblade/admin/security/InternalAuthorizationManager.java

@@ -0,0 +1,75 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.admin.security;
+
+import org.springblade.core.launch.utils.INetUtil;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.security.authorization.AuthorizationDecision;
+import org.springframework.security.authorization.ReactiveAuthorizationManager;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.web.server.authorization.AuthorizationContext;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Mono;
+
+import java.net.InetSocketAddress;
+import java.util.Optional;
+
+/**
+ * 内网认证管理,内网放行,外网认证
+ *
+ * @author L.cm
+ */
+public class InternalAuthorizationManager implements ReactiveAuthorizationManager<AuthorizationContext> {
+	private static final String HEADER_X_FORWARDED_FOR = "X-Forwarded-For";
+
+	@Override
+	public Mono<AuthorizationDecision> check(Mono<Authentication> authentication, AuthorizationContext context) {
+		return Mono.just(getAuthorizationDecision(context));
+	}
+
+	private static AuthorizationDecision getAuthorizationDecision(AuthorizationContext context) {
+		return new AuthorizationDecision(isInternalNet(context));
+	}
+
+	/**
+	 * 判断是否内网 ip 请求
+	 *
+	 * @param context AuthorizationContext
+	 * @return 是否内网 ip
+	 */
+	private static boolean isInternalNet(AuthorizationContext context) {
+		ServerHttpRequest request = Optional.ofNullable(context)
+			.map(AuthorizationContext::getExchange)
+			.map(ServerWebExchange::getRequest)
+			.orElse(null);
+		if (request == null) {
+			return false;
+		}
+		HttpHeaders headers = request.getHeaders();
+		// 如果没有 X-Forwarded-For 代表为 admin 拉取
+		if (!headers.containsKey(HEADER_X_FORWARDED_FOR)) {
+			return true;
+		}
+		return Optional.of(request)
+			.map(ServerHttpRequest::getRemoteAddress)
+			.map(InetSocketAddress::getAddress)
+			.map(INetUtil::isInternalIp)
+			.orElse(false);
+	}
+
+}

+ 45 - 0
blade-ops/blade-admin/src/main/resources/bootstrap.yml

@@ -0,0 +1,45 @@
+server:
+  port: 7002
+  undertow:
+    threads:
+      # 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程
+      io: 16
+      # 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载
+      worker: 400
+    # 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理
+    buffer-size: 1024
+    # 是否分配的直接内存
+    direct-buffers: true
+
+spring:
+  boot:
+    admin:
+      discovery:
+        ignored-services:
+          - consul
+          - serverAddr
+      ui:
+        title: BladeX Monitor
+        external-views:
+          - label: 架构官网
+            url: https://bladex.vip/
+            order: 1
+            iframe: true
+      ## 用于内网安全,判断 admin proxy
+      instance-proxy:
+        ignored-headers: "X-Forwarded-For"
+  security:
+    user:
+      name: blade
+      password: blade
+
+# 监控的相关配置
+monitor:
+  ding-talk:
+    enabled: false
+    # 用于自定义域名,默认会自动填充为 http://ip:port
+    link: http://localhost:${server.port}
+    # 钉钉配置的令牌
+    access-token: xxx
+    # 如果采用密钥形式,需要添加,否则需要去掉该参数
+    secret:

+ 289 - 0
blade-ops/blade-develop/blade-develop.iml

@@ -0,0 +1,289 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
+  <component name="FacetManager">
+    <facet type="Spring" name="Spring">
+      <configuration />
+    </facet>
+    <facet type="web" name="Web">
+      <configuration>
+        <webroots />
+        <sourceRoots>
+          <root url="file://$MODULE_DIR$/src/main/java" />
+          <root url="file://$MODULE_DIR$/src/main/resources" />
+        </sourceRoots>
+      </configuration>
+    </facet>
+  </component>
+  <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
+    <output url="file://$MODULE_DIR$/target/classes" />
+    <output-test url="file://$MODULE_DIR$/target/test-classes" />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
+      <excludeFolder url="file://$MODULE_DIR$/target" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-core-boot:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-core-context:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-core-db:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-jdbc:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.zaxxer:HikariCP:3.4.5" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-jdbc:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-tx:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.baomidou:mybatis-plus-boot-starter:3.5.1" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-autoconfigure:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba:druid-spring-boot-starter:1.2.8" level="project" />
+    <orderEntry type="library" name="Maven: mysql:mysql-connector-java:8.0.22" level="project" />
+    <orderEntry type="library" name="Maven: com.google.protobuf:protobuf-java:3.14.0" level="project" />
+    <orderEntry type="library" name="Maven: com.oracle:ojdbc7:12.2.0.1" level="project" />
+    <orderEntry type="library" name="Maven: org.postgresql:postgresql:42.2.22" level="project" />
+    <orderEntry type="library" name="Maven: org.checkerframework:checker-qual:3.5.0" level="project" />
+    <orderEntry type="library" name="Maven: com.microsoft.sqlserver:mssql-jdbc:8.4.1.jre8" level="project" />
+    <orderEntry type="library" name="Maven: com.dameng:DmJdbcDriver18:8.1.2.79" level="project" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-core-secure:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-core-cloud:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: io.github.openfeign:feign-okhttp:10.12" level="project" />
+    <orderEntry type="library" name="Maven: com.squareup.okhttp3:okhttp:4.9.3" level="project" />
+    <orderEntry type="library" name="Maven: com.squareup.okio:okio:2.8.0" level="project" />
+    <orderEntry type="library" name="Maven: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.72" level="project" />
+    <orderEntry type="library" name="Maven: org.jetbrains.kotlin:kotlin-stdlib:1.3.72" level="project" />
+    <orderEntry type="library" name="Maven: org.jetbrains:annotations:13.0" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-starter-netflix-hystrix:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-netflix-hystrix:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-netflix-ribbon:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-netflix-archaius:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-starter-netflix-archaius:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: commons-configuration:commons-configuration:1.8" level="project" />
+    <orderEntry type="library" name="Maven: com.netflix.hystrix:hystrix-core:1.5.18" level="project" />
+    <orderEntry type="library" name="Maven: com.netflix.hystrix:hystrix-serialization:1.5.18" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: com.fasterxml.jackson.module:jackson-module-afterburner:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: com.netflix.hystrix:hystrix-metrics-event-stream:1.5.18" level="project" />
+    <orderEntry type="library" name="Maven: com.netflix.hystrix:hystrix-javanica:1.5.18" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: org.apache.commons:commons-lang3:3.10" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: org.ow2.asm:asm:5.0.4" level="project" />
+    <orderEntry type="library" name="Maven: io.reactivex:rxjava-reactive-streams:1.2.1" level="project" />
+    <orderEntry type="library" name="Maven: org.reactivestreams:reactive-streams:1.0.3" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-actuator:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-actuator-autoconfigure:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-actuator:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: de.codecentric:spring-boot-admin-starter-client:2.3.1" level="project" />
+    <orderEntry type="library" name="Maven: de.codecentric:spring-boot-admin-client:2.3.1" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-discovery:2.2.7.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.cloud:spring-cloud-alibaba-commons:2.2.7.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.spring:spring-context-support:1.0.10" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-context:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-config:2.2.7.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.nacos:nacos-client:2.0.4" level="project" />
+    <orderEntry type="library" name="Maven: commons-codec:commons-codec:1.14" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-core:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-databind:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: org.apache.httpcomponents:httpasyncclient:4.1.4" level="project" />
+    <orderEntry type="library" name="Maven: org.apache.httpcomponents:httpcore:4.4.14" level="project" />
+    <orderEntry type="library" name="Maven: org.apache.httpcomponents:httpcore-nio:4.4.14" level="project" />
+    <orderEntry type="library" name="Maven: org.apache.httpcomponents:httpclient:4.5.13" level="project" />
+    <orderEntry type="library" name="Maven: org.reflections:reflections:0.9.11" level="project" />
+    <orderEntry type="library" name="Maven: org.javassist:javassist:3.21.0-GA" level="project" />
+    <orderEntry type="library" name="Maven: io.prometheus:simpleclient:0.5.0" level="project" />
+    <orderEntry type="library" name="Maven: org.yaml:snakeyaml:1.26" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.cloud:spring-cloud-starter-alibaba-sentinel:2.2.7.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.csp:sentinel-transport-simple-http:1.8.1" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.csp:sentinel-transport-common:1.8.1" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.csp:sentinel-datasource-extension:1.8.1" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba:fastjson:1.2.80" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.csp:sentinel-annotation-aspectj:1.8.1" level="project" />
+    <orderEntry type="library" name="Maven: org.aspectj:aspectjrt:1.9.6" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.cloud:spring-cloud-circuitbreaker-sentinel:2.2.7.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.csp:sentinel-reactor-adapter:1.8.1" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.csp:sentinel-spring-webflux-adapter:1.8.1" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.csp:sentinel-spring-webmvc-adapter:1.8.1" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.csp:sentinel-parameter-flow-control:1.8.1" level="project" />
+    <orderEntry type="library" name="Maven: com.googlecode.concurrentlinkedhashmap:concurrentlinkedhashmap-lru:1.4.2" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.csp:sentinel-cluster-server-default:1.8.1" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.csp:sentinel-cluster-common-default:1.8.1" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.csp:sentinel-cluster-client-default:1.8.1" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.cloud:spring-cloud-alibaba-sentinel-datasource:2.2.7.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-starter-cache:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-cache:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-context-support:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-starter-redis:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-starter-jwt:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: io.jsonwebtoken:jjwt-impl:0.11.2" level="project" />
+    <orderEntry type="library" name="Maven: io.jsonwebtoken:jjwt-api:0.11.2" level="project" />
+    <orderEntry type="library" name="Maven: io.jsonwebtoken:jjwt-jackson:0.11.2" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-data-redis:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.data:spring-data-redis:2.3.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.data:spring-data-keyvalue:2.3.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.data:spring-data-commons:2.3.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-oxm:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: io.lettuce:lettuce-core:5.3.7.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.redisson:redisson:3.11.6" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-common:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-codec:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-buffer:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-transport:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-resolver:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-resolver-dns:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-codec-dns:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.netty:netty-handler:4.1.65.Final" level="project" />
+    <orderEntry type="library" name="Maven: javax.cache:cache-api:1.1.1" level="project" />
+    <orderEntry type="library" name="Maven: io.projectreactor:reactor-core:3.3.17.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: io.reactivex.rxjava2:rxjava:2.2.21" level="project" />
+    <orderEntry type="library" name="Maven: de.ruedigermoeller:fst:2.57" level="project" />
+    <orderEntry type="library" name="Maven: org.objenesis:objenesis:2.6" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: net.bytebuddy:byte-buddy:1.10.22" level="project" />
+    <orderEntry type="library" name="Maven: org.jodd:jodd-bean:5.0.13" level="project" />
+    <orderEntry type="library" name="Maven: org.jodd:jodd-core:5.0.13" level="project" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-starter-log:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.baomidou:mybatis-plus:3.5.1" level="project" />
+    <orderEntry type="library" name="Maven: net.logstash.logback:logstash-logback-encoder:6.2" level="project" />
+    <orderEntry type="library" name="Maven: org.codehaus.janino:janino:3.0.15" level="project" />
+    <orderEntry type="library" name="Maven: org.codehaus.janino:commons-compiler:3.1.4" level="project" />
+    <orderEntry type="library" name="Maven: org.hibernate.validator:hibernate-validator:6.1.7.Final" level="project" />
+    <orderEntry type="library" name="Maven: jakarta.validation:jakarta.validation-api:2.0.2" level="project" />
+    <orderEntry type="library" name="Maven: org.jboss.logging:jboss-logging:3.4.2.Final" level="project" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-starter-mybatis:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.mybatis:mybatis-typehandlers-jsr310:1.0.2" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-aop:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-logging:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: ch.qos.logback:logback-classic:1.2.11" level="project" />
+    <orderEntry type="library" name="Maven: ch.qos.logback:logback-core:1.2.11" level="project" />
+    <orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-to-slf4j:2.17.2" level="project" />
+    <orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-api:2.17.2" level="project" />
+    <orderEntry type="library" name="Maven: org.slf4j:jul-to-slf4j:1.7.30" level="project" />
+    <orderEntry type="library" name="Maven: jakarta.annotation:jakarta.annotation-api:1.3.5" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-aop:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-beans:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.aspectj:aspectjweaver:1.9.6" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.retry:spring-retry:1.2.5.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-core:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-jcl:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-starter-develop:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-core-tool:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: com.google.guava:guava:30.1.1-jre" level="project" />
+    <orderEntry type="library" name="Maven: com.google.guava:failureaccess:1.0.1" level="project" />
+    <orderEntry type="library" name="Maven: com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava" level="project" />
+    <orderEntry type="library" name="Maven: com.google.code.findbugs:jsr305:3.0.2" level="project" />
+    <orderEntry type="library" name="Maven: com.google.errorprone:error_prone_annotations:2.5.1" level="project" />
+    <orderEntry type="library" name="Maven: com.google.j2objc:j2objc-annotations:1.3" level="project" />
+    <orderEntry type="library" name="Maven: io.protostuff:protostuff-core:1.6.0" level="project" />
+    <orderEntry type="library" name="Maven: io.protostuff:protostuff-api:1.6.0" level="project" />
+    <orderEntry type="library" name="Maven: io.protostuff:protostuff-runtime:1.6.0" level="project" />
+    <orderEntry type="library" name="Maven: io.protostuff:protostuff-collectionschema:1.6.0" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.module:jackson-module-jaxb-annotations:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: jakarta.xml.bind:jakarta.xml.bind-api:2.3.3" level="project" />
+    <orderEntry type="library" name="Maven: jakarta.activation:jakarta.activation-api:1.2.2" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-validation:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.glassfish:jakarta.el:3.0.3" level="project" />
+    <orderEntry type="library" name="Maven: com.baomidou:mybatis-plus-generator:3.5.2" level="project" />
+    <orderEntry type="library" name="Maven: com.baomidou:mybatis-plus-extension:3.5.1" level="project" />
+    <orderEntry type="library" name="Maven: com.baomidou:mybatis-plus-core:3.5.1" level="project" />
+    <orderEntry type="library" name="Maven: com.baomidou:mybatis-plus-annotation:3.5.1" level="project" />
+    <orderEntry type="library" name="Maven: com.github.jsqlparser:jsqlparser:4.3" level="project" />
+    <orderEntry type="library" name="Maven: org.mybatis:mybatis:3.5.9" level="project" />
+    <orderEntry type="library" name="Maven: org.mybatis:mybatis-spring:2.0.6" level="project" />
+    <orderEntry type="library" name="Maven: org.apache.velocity:velocity:1.7" level="project" />
+    <orderEntry type="library" name="Maven: commons-collections:commons-collections:3.2.2" level="project" />
+    <orderEntry type="library" name="Maven: commons-lang:commons-lang:2.6" level="project" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-starter-swagger:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-starter-auth:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.github.xiaoymin:knife4j-micro-spring-boot-starter:2.0.9" level="project" />
+    <orderEntry type="library" name="Maven: com.github.xiaoymin:knife4j-spring-boot-autoconfigure:2.0.9" level="project" />
+    <orderEntry type="library" name="Maven: com.github.xiaoymin:knife4j-spring:2.0.9" level="project" />
+    <orderEntry type="library" name="Maven: com.github.xiaoymin:knife4j-annotations:2.0.9" level="project" />
+    <orderEntry type="library" name="Maven: com.github.xiaoymin:knife4j-core:2.0.9" level="project" />
+    <orderEntry type="library" name="Maven: io.springfox:springfox-bean-validators:2.10.5" level="project" />
+    <orderEntry type="library" name="Maven: io.springfox:springfox-spring-webmvc:2.10.5" level="project" />
+    <orderEntry type="module" module-name="blade-common" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-core-launch:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-web:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-json:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.module:jackson-module-parameter-names:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-webmvc:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-expression:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-undertow:2.3.12.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: io.undertow:undertow-core:2.1.7.Final" level="project" />
+    <orderEntry type="library" name="Maven: org.jboss.xnio:xnio-api:3.8.0.Final" level="project" />
+    <orderEntry type="library" name="Maven: org.wildfly.common:wildfly-common:1.5.2.Final" level="project" />
+    <orderEntry type="library" name="Maven: org.wildfly.client:wildfly-client-config:1.0.1.Final" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: org.jboss.xnio:xnio-nio:3.8.0.Final" level="project" />
+    <orderEntry type="library" name="Maven: org.jboss.threads:jboss-threads:3.1.0.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.undertow:undertow-servlet:2.1.7.Final" level="project" />
+    <orderEntry type="library" name="Maven: org.jboss.spec.javax.annotation:jboss-annotations-api_1.3_spec:2.0.1.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.undertow:undertow-websockets-jsr:2.1.7.Final" level="project" />
+    <orderEntry type="library" name="Maven: org.jboss.spec.javax.websocket:jboss-websocket-api_1.1_spec:2.0.0.Final" level="project" />
+    <orderEntry type="library" name="Maven: jakarta.servlet:jakarta.servlet-api:4.0.4" level="project" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-starter-ribbon:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-starter-netflix-ribbon:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.netflix.ribbon:ribbon:2.3.0" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: com.netflix.ribbon:ribbon-transport:2.3.0" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: io.reactivex:rxnetty-contexts:0.4.9" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: io.reactivex:rxnetty-servo:0.4.9" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: javax.inject:javax.inject:1" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: io.reactivex:rxnetty:0.4.9" level="project" />
+    <orderEntry type="library" name="Maven: com.netflix.ribbon:ribbon-core:2.3.0" level="project" />
+    <orderEntry type="library" name="Maven: com.netflix.ribbon:ribbon-httpclient:2.3.0" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: com.sun.jersey:jersey-client:1.19.1" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: com.sun.jersey:jersey-core:1.19.1" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: javax.ws.rs:jsr311-api:1.1.1" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: com.sun.jersey.contribs:jersey-apache-client4:1.19.1" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: com.netflix.servo:servo-core:0.12.21" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: com.netflix.netflix-commons:netflix-commons-util:0.3.0" level="project" />
+    <orderEntry type="library" name="Maven: com.netflix.ribbon:ribbon-loadbalancer:2.3.0" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: com.netflix.netflix-commons:netflix-statistics:0.1.1" level="project" />
+    <orderEntry type="library" name="Maven: io.reactivex:rxjava:1.3.8" level="project" />
+    <orderEntry type="module" module-name="blade-dict-api" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-starter-tenant:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: com.baomidou:dynamic-datasource-spring-boot-starter:3.3.6" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-starter-openfeign:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-starter:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.security:spring-security-rsa:1.0.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.bouncycastle:bcpkix-jdk15on:1.59" level="project" />
+    <orderEntry type="library" name="Maven: org.bouncycastle:bcprov-jdk15on:1.59" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-openfeign-core:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: io.github.openfeign.form:feign-form-spring:3.8.0" level="project" />
+    <orderEntry type="library" name="Maven: io.github.openfeign.form:feign-form:3.8.0" level="project" />
+    <orderEntry type="library" name="Maven: commons-fileupload:commons-fileupload:1.3.3" level="project" />
+    <orderEntry type="library" name="Maven: commons-io:commons-io:2.6" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-web:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-commons:2.2.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.security:spring-security-crypto:5.3.9.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: io.github.openfeign:feign-core:10.12" level="project" />
+    <orderEntry type="library" name="Maven: io.github.openfeign:feign-slf4j:10.12" level="project" />
+    <orderEntry type="library" name="Maven: io.github.openfeign:feign-hystrix:10.12" level="project" />
+    <orderEntry type="library" name="Maven: com.netflix.archaius:archaius-core:0.7.7" level="project" />
+    <orderEntry type="library" name="Maven: io.springfox:springfox-swagger2:2.10.5" level="project" />
+    <orderEntry type="library" name="Maven: io.swagger:swagger-annotations:1.5.20" level="project" />
+    <orderEntry type="library" name="Maven: io.springfox:springfox-spi:2.10.5" level="project" />
+    <orderEntry type="library" name="Maven: io.springfox:springfox-core:2.10.5" level="project" />
+    <orderEntry type="library" name="Maven: io.springfox:springfox-schema:2.10.5" level="project" />
+    <orderEntry type="library" name="Maven: io.springfox:springfox-swagger-common:2.10.5" level="project" />
+    <orderEntry type="library" name="Maven: io.springfox:springfox-spring-web:2.10.5" level="project" />
+    <orderEntry type="library" name="Maven: io.github.classgraph:classgraph:4.1.7" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml:classmate:1.5.1" level="project" />
+    <orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.7.30" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.plugin:spring-plugin-core:2.0.0.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework:spring-context:5.2.15.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.plugin:spring-plugin-metadata:2.0.0.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: org.mapstruct:mapstruct:1.3.1.Final" level="project" />
+    <orderEntry type="library" name="Maven: io.swagger:swagger-models:1.6.2" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.11.4" level="project" />
+    <orderEntry type="library" name="Maven: org.springblade:blade-starter-metrics:2.9.1.RELEASE" level="project" />
+    <orderEntry type="library" name="Maven: io.micrometer:micrometer-core:1.6.3" level="project" />
+    <orderEntry type="library" name="Maven: org.hdrhistogram:HdrHistogram:2.1.12" level="project" />
+    <orderEntry type="library" scope="RUNTIME" name="Maven: org.latencyutils:LatencyUtils:2.0.3" level="project" />
+    <orderEntry type="library" name="Maven: io.micrometer:micrometer-registry-prometheus:1.6.3" level="project" />
+    <orderEntry type="library" name="Maven: io.prometheus:simpleclient_common:0.9.0" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba.csp:sentinel-core:1.8.3" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba:druid:1.2.8" level="project" />
+    <orderEntry type="library" name="Maven: javax.annotation:javax.annotation-api:1.3.2" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.projectlombok:lombok:1.18.22" level="project" />
+  </component>
+</module>

+ 53 - 0
blade-ops/blade-develop/pom.xml

@@ -0,0 +1,53 @@
+<?xml version="1.0"?>
+<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>
+        <groupId>org.springblade</groupId>
+        <artifactId>blade-ops</artifactId>
+        <version>2.9.1.RELEASE</version>
+    </parent>
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>blade-develop</artifactId>
+    <name>${project.artifactId}</name>
+    <version>${bladex.project.version}</version>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <!--Blade-->
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-core-boot</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-starter-develop</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-starter-swagger</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-common</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-dict-api</artifactId>
+            <version>${bladex.project.version}</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-antrun-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 38 - 0
blade-ops/blade-develop/src/main/java/org/springblade/develop/DevelopApplication.java

@@ -0,0 +1,38 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.develop;
+
+import org.springblade.core.cloud.feign.EnableBladeFeign;
+import org.springblade.core.launch.BladeApplication;
+import org.springblade.core.launch.constant.AppConstant;
+import org.springframework.cloud.client.SpringCloudApplication;
+
+/**
+ * Develop启动器
+ *
+ * @author Chill
+ */
+@EnableBladeFeign
+@SpringCloudApplication
+public class DevelopApplication {
+
+	public static void main(String[] args) {
+		BladeApplication.run(AppConstant.APPLICATION_DEVELOP_NAME, DevelopApplication.class, args);
+	}
+
+}
+

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است