pull/3/head
tanghc 4 years ago
parent ee30f18d53
commit 0fea955db9
  1. 1
      .gitignore
  2. 13
      changelog.md
  3. 69
      doc/docs/_sidebar.md
  4. 17
      doc/docs/files/10010_快速体验.md
  5. 107
      doc/docs/files/10020_新增接口.md
  6. 3
      doc/docs/files/10030_业务参数校验.md
  7. 2
      doc/docs/files/10041_编写文档.md
  8. 78
      doc/docs/files/10070_easyopen支持.md
  9. 15
      doc/docs/files/10089_自定义校验token.md
  10. 4
      doc/docs/files/10090_路由授权.md
  11. 16
      doc/docs/files/10096_使用SpringCloudGateway.md
  12. 24
      doc/docs/files/10100_提供restful接口.md
  13. 6
      doc/docs/files/10104_文件上传.md
  14. 4
      doc/docs/files/10112_使用eureka.md
  15. 86
      doc/docs/files/10113_扩展其它注册中心.md
  16. 21
      doc/docs/files/90010_原理分析之@ApiMapping.md
  17. 23
      doc/docs/files/90099_2.x升3.x注意事项.md
  18. 4
      doc/docs/files/90100_常见问题.md
  19. 2
      pom.xml
  20. 5
      sop-admin/sop-admin-server/pom.xml
  21. 11
      sop-admin/sop-admin-server/readme.md
  22. 2
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/system/SystemApi.java
  23. 19
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/bean/ServiceInfo.java
  24. 10
      sop-admin/sop-admin-server/src/main/resources/META-INF/sop-admin.properties
  25. 4
      sop-admin/sop-admin-server/src/main/resources/application-dev.properties
  26. 23
      sop-admin/sop-admin-server/src/test/java/com/gitee/sop/adminserver/AccountTest.java
  27. 5
      sop-auth/src/main/java/com/gitee/sop/sopauth/controller/OAuth2Controller.java
  28. 4
      sop-auth/src/main/resources/application-dev.properties
  29. 4
      sop-common/pom.xml
  30. 4
      sop-common/readme.md
  31. 10
      sop-common/sop-bridge-eureka/pom.xml
  32. 31
      sop-common/sop-bridge-eureka/src/main/java/com/gitee/sop/bridge/SopRegisterAutoConfiguration.java
  33. 5
      sop-common/sop-bridge-eureka/src/main/java/com/gitee/sop/bridge/route/EurekaRegistryListener.java
  34. 16
      sop-common/sop-bridge-nacos/pom.xml
  35. 46
      sop-common/sop-bridge-nacos/src/main/java/com/gitee/sop/bridge/SopRegisterAutoConfiguration.java
  36. 4
      sop-common/sop-bridge-nacos/src/main/java/com/gitee/sop/bridge/route/NacosRegistryListener.java
  37. 5
      sop-common/sop-bridge-nacos/src/main/java/com/gitee/sop/bridge/route/NacosServiceHolder.java
  38. 2
      sop-common/sop-bridge-nacos/src/main/resources/sop-bridge.properties
  39. 24
      sop-common/sop-bridge-zuul/src/main/java/com/gitee/sop/bridge/SopGatewayAutoConfiguration.java
  40. 12
      sop-common/sop-gateway-common/pom.xml
  41. 19
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ApiConfig.java
  42. 1
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/SopConstants.java
  43. 60
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/config/AbstractConfiguration.java
  44. 3
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/config/SopGatewayAutoConfiguration.java
  45. 2
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/config/SopGatewayEnvironmentPostProcessor.java
  46. 44
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/easyopen/EasyopenResultExecutor.java
  47. 33
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/easyopen/EasyopenSigner.java
  48. 38
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/easyopen/EasyopenZuulConfiguration.java
  49. 9
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/ServerWebExchangeUtil.java
  50. 2
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/configuration/BaseGatewayConfiguration.java
  51. 5
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/GatewayModifyResponseGatewayFilter.java
  52. 14
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/IndexFilter.java
  53. 7
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/LimitFilter.java
  54. 2
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/result/GatewayResultExecutor.java
  55. 2
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/loadbalancer/ServiceGrayConfig.java
  56. 27
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/loadbalancer/SopPropertiesFactory.java
  57. 6
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/DefaultEnvGrayManager.java
  58. 2
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/EnvGrayManager.java
  59. 17
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/EnvironmentKeys.java
  60. 9
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/ResultExecutorForZuul.java
  61. 7
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/ServiceRouteListener.java
  62. 27
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/RequestUtil.java
  63. 37
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/RequestContextUtil.java
  64. 97
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/ValidateService.java
  65. 137
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/ZuulContext.java
  66. 16
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/configuration/AlipayZuulConfiguration.java
  67. 176
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/configuration/BaseZuulConfiguration.java
  68. 23
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/configuration/TaobaoZuulConfiguration.java
  69. 18
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/configuration/WebappZuulConfiguration.java
  70. 68
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/controller/ConfigChannelController.java
  71. 23
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/controller/ErrorLogController.java
  72. 53
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/controller/ZuulErrorController.java
  73. 84
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/controller/ZuulIndexController.java
  74. 21
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/controller/ZuulMonitorController.java
  75. 130
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/filter/BaseZuulFilter.java
  76. 60
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/filter/ErrorFilter.java
  77. 14
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/filter/FormBodyWrapperFilterExt.java
  78. 75
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/filter/PostResultFilter.java
  79. 53
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/filter/PreEnvGrayFilter.java
  80. 31
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/filter/PreHttpServletRequestWrapperFilter.java
  81. 118
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/filter/PreLimitFilter.java
  82. 42
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/filter/PreParameterFormatterFilter.java
  83. 30
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/filter/PreValidateFilter.java
  84. 14
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/filter/Servlet30WrapperFilterExt.java
  85. 55
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/loadbalancer/EnvironmentServerChooser.java
  86. 24
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/loadbalancer/ZuulLoadBalanceServerChooser.java
  87. 67
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/param/ZuulParamBuilder.java
  88. 141
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/param/ZuulParameterUtil.java
  89. 115
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/result/ZuulResultExecutor.java
  90. 63
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/route/SopRouteLocator.java
  91. 18
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/route/ZuulForwardChooser.java
  92. 34
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/route/ZuulRouteCache.java
  93. 98
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/route/ZuulRouteRepository.java
  94. 18
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/route/ZuulTargetRoute.java
  95. 39
      sop-common/sop-gateway-common/src/main/resources/sop-bridge.properties
  96. 92
      sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/annotation/ApiMapping.java
  97. 16
      sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/annotation/Open.java
  98. 108
      sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/bean/OpenContext.java
  99. 124
      sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/bean/OpenContextImpl.java
  100. 4
      sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/bean/ServiceApiInfo.java
  101. Some files were not shown because too many files have changed in this diff Show More

1
.gitignore vendored

@ -15,6 +15,7 @@
*.iws
*.iml
*.ipr
*.png
### NetBeans ###
/nbproject/private/

@ -1,5 +1,18 @@
# changelog
## 4.0.0(不兼容3.x)
- 新增@Open注解,代替ApiMapping和ApiAbility
- service接入减少代码入侵
- 修复admin密码存储规则
### 不兼容部分
- 移除ApiMapping和ApiAbility,改为Open注解
- 移除OpenContext
- 移除zuul
- 移除对easyopen支持
## 3.2.1
- 强化RouteInterceptorContext,可获取微服务信息

@ -1,39 +1,34 @@
* [首页](/?t=1595832340981)
* [首页](/?t=1595931646391)
* 开发文档
* [快速体验](files/10010_快速体验.md?t=1595832340983)
* [项目接入到SOP](files/10011_项目接入到SOP.md?t=1595832341000)
* [新增接口](files/10020_新增接口.md?t=1595832341000)
* [开发流程](files/10021_开发流程.md?t=1595832341001)
* [业务参数校验](files/10030_业务参数校验.md?t=1595832341001)
* [错误处理](files/10040_错误处理.md?t=1595832341001)
* [编写文档](files/10041_编写文档.md?t=1595832341001)
* [接口交互详解](files/10050_接口交互详解.md?t=1595832341001)
* [easyopen支持](files/10070_easyopen支持.md?t=1595832341001)
* [使用签名校验工具](files/10080_使用签名校验工具.md?t=1595832341001)
* [ISV管理](files/10085_ISV管理.md?t=1595832341001)
* [自定义返回结果](files/10087_自定义返回结果.md?t=1595832341001)
* [自定义过滤器](files/10088_自定义过滤器.md?t=1595832341001)
* [自定义校验token](files/10089_自定义校验token.md?t=1595832341002)
* [网关拦截器](files/10090_网关拦截器.md?t=1595832341002)
* [路由授权](files/10090_路由授权.md?t=1595832341002)
* [接口限流](files/10092_接口限流.md?t=1595832341002)
* [路由监控](files/10093_路由监控.md?t=1595832341002)
* [SDK开发](files/10095_SDK开发.md?t=1595832341002)
* [使用SpringCloudGateway](files/10096_使用SpringCloudGateway.md?t=1595832341002)
* [应用授权](files/10097_应用授权.md?t=1595832341003)
* [提供restful接口](files/10100_提供restful接口.md?t=1595832341003)
* [文件上传](files/10104_文件上传.md?t=1595832341003)
* [配置Sleuth链路追踪](files/10109_配置Sleuth链路追踪.md?t=1595832341003)
* [预发布灰度发布](files/10110_预发布灰度发布.md?t=1595832341003)
* [动态修改请求参数](files/10111_动态修改请求参数.md?t=1595832341003)
* [使用eureka](files/10112_使用eureka.md?t=1595832341003)
* [扩展其它注册中心](files/10113_扩展其它注册中心.md?t=1595832341003)
* [快速体验](files/10010_快速体验.md?t=1595931646394)
* [项目接入到SOP](files/10011_项目接入到SOP.md?t=1595931646412)
* [新增接口](files/10020_新增接口.md?t=1595931646412)
* [开发流程](files/10021_开发流程.md?t=1595931646412)
* [业务参数校验](files/10030_业务参数校验.md?t=1595931646412)
* [错误处理](files/10040_错误处理.md?t=1595931646412)
* [编写文档](files/10041_编写文档.md?t=1595931646412)
* [接口交互详解](files/10050_接口交互详解.md?t=1595931646412)
* [使用签名校验工具](files/10080_使用签名校验工具.md?t=1595931646413)
* [ISV管理](files/10085_ISV管理.md?t=1595931646413)
* [自定义返回结果](files/10087_自定义返回结果.md?t=1595931646413)
* [自定义过滤器](files/10088_自定义过滤器.md?t=1595931646413)
* [自定义校验token](files/10089_自定义校验token.md?t=1595931646413)
* [网关拦截器](files/10090_网关拦截器.md?t=1595931646413)
* [路由授权](files/10090_路由授权.md?t=1595931646413)
* [接口限流](files/10092_接口限流.md?t=1595931646413)
* [路由监控](files/10093_路由监控.md?t=1595931646413)
* [SDK开发](files/10095_SDK开发.md?t=1595931646413)
* [应用授权](files/10097_应用授权.md?t=1595931646414)
* [提供restful接口](files/10100_提供restful接口.md?t=1595931646414)
* [文件上传](files/10104_文件上传.md?t=1595931646414)
* [配置Sleuth链路追踪](files/10109_配置Sleuth链路追踪.md?t=1595931646414)
* [预发布灰度发布](files/10110_预发布灰度发布.md?t=1595931646414)
* [动态修改请求参数](files/10111_动态修改请求参数.md?t=1595931646414)
* [使用eureka](files/10112_使用eureka.md?t=1595931646414)
* 原理分析
* [网关性能测试](files/90001_网关性能测试.md?t=1595832341004)
* [原理分析之@ApiMapping](files/90010_原理分析之@ApiMapping.md?t=1595832341004)
* [原理分析之如何存储路由](files/90011_原理分析之如何存储路由.md?t=1595832341004)
* [原理分析之如何路由](files/90012_原理分析之如何路由.md?t=1595832341004)
* [原理分析之文档归纳](files/90013_原理分析之文档归纳.md?t=1595832341004)
* [原理分析之预发布灰度发布](files/90014_原理分析之预发布灰度发布.md?t=1595832341004)
* [2.x升3.x注意事项](files/90099_2.x升3.x注意事项.md?t=1595832341004)
* [常见问题](files/90100_常见问题.md?t=1595832341004)
* [网关性能测试](files/90001_网关性能测试.md?t=1595931646414)
* [原理分析之如何存储路由](files/90011_原理分析之如何存储路由.md?t=1595931646415)
* [原理分析之如何路由](files/90012_原理分析之如何路由.md?t=1595931646415)
* [原理分析之文档归纳](files/90013_原理分析之文档归纳.md?t=1595931646415)
* [原理分析之预发布灰度发布](files/90014_原理分析之预发布灰度发布.md?t=1595931646415)
* [常见问题](files/90100_常见问题.md?t=1595931646415)

@ -30,20 +30,3 @@
- 修改sop-website下的application-dev.properties相关配置
- 运行WebsiteServerApplication.java
- 访问http://localhost:8083
## 基本配置
在`sop-gateway`下的application-dev.properties配置,各项配置说明如下
```properties
# 忽略验证,设置true,则所有接口不会进行签名校验,默认false
sop.api-config.ignore-validate=false
# 是否对结果进行合并,默认true
sop.api-config.merge-result=true
# 显示返回sign,默认true
sop.api-config.show-return-sign=true
# 是否开启限流功能,默认true
sop.api-config.open-limit=true
# 请求超时时间,默认5分钟,即允许在5分钟内重复请求,默认300
sop.api-config.timeout-seconds=300
```

@ -1,21 +1,29 @@
# 新增接口
以story服务为例,新增一个获取故事内容接口
假设要对下面这个接口提供开放能力。
- 在controller下新建一个类,StoryDemoController.java
- 加上`@RestController`注解
```java
@RestController
public class StoryDemoController {
@RequestMapping("/story/get")
public StoryResult getStory() {
StoryResult result = new StoryResult();
result.setId(1L);
result.setName("海底小纵队");
return result;
}
}
```
- 新增一个接口
只需要在方法上新增一个`@Open`注解,指定接口名即可
```java
@ApiMapping(value = "story.demo.get")
// 添加一个@Open注解
@Open("story.demo.get")
@RequestMapping("/story/get")
public StoryResult getStory() {
StoryResult result = new StoryResult();
result.setId(1L);
@ -24,13 +32,9 @@ public StoryResult getStory() {
}
```
这里的`@ApiMapping`注解作用同`@RequestMapping`注解,可以理解为是它的扩展
value就是接口名,对应客户端的`method`参数
如果要加上版本号,指定`version`参数:`@Open(value = "story.demo.get", version = "2.0")`
如果要加上版本号,指定`version`参数:`@ApiMapping(value = "story.demo.get", version = "2.0")`
- 重启story服务,这样接口就可以使用了。
- 重启服务,这样接口就可以使用了。
## 绑定业务参数
@ -43,7 +47,8 @@ value就是接口名,对应客户端的`method`参数
其中biz_content部分是我们想要的,在方法上申明一个对象,对应biz_content中的内容即可完成参数绑定,并且对参数进行JSR-303校验。
```java
@ApiMapping(value = "goods.add")
@Open("goods.add")
@RequestMapping("/goods/add")
public Object addGoods(GoodsParam param) {
return param;
}
@ -58,40 +63,14 @@ public class GoodsParam {
一般情况下,只需要获取业务参数即可,如果想要获取更多的参数,可在后面跟一个`HttpServletRequest`对象。
```java
@ApiMapping(value = "goods.add")
@Open("goods.add")
@RequestMapping("/goods/add")
public Object addGoods(GoodsParam param, HttpServletRequest request) {
System.out.println(request.getParameter("method"));
return param;
}
```
- 方式2
```java
@ApiMapping(value = "story.get", version = "2.2")
public Story getStory22(OpenContext<Story> openContext) {
// 业务参数
Story bizObject = openContext.getBizObject();
// 获取appid,更多方法查看OpenContext类
String appId = openContext.getAppId();
System.out.println(appId);
return bizObject;
}
```
另一种方式,OpenContext泛型参数填bizObject类,调用openContext.getBizObject()可直接获得对象
此方式等价于:
```java
@ApiMapping(value = "story.get", version = "2.2")
public Story getStory22(Story bizObject) {
OpenContext openContext = ServiceContext.getCurrentContext().getOpenContext();
String appId = openContext.getAppId();
System.out.println(appId);
return bizObject;
}
```
## 接口命名
@ -122,13 +101,13 @@ public class StoryDemoTest extends TestBase {
// 公共请求参数
Map<String, String> params = new HashMap<String, String>();
params.put("app_id", appId);
// 这里对应@ApiMapping.value属性
// 这里对应@Open.value属性
params.put("method", "story.demo.get");
params.put("format", "json");
params.put("charset", "utf-8");
params.put("sign_type", "RSA2");
params.put("timestamp", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
// 这里对应@ApiMapping.version属性
// 这里对应@Open.version属性
params.put("version", "1.0");
// 业务参数
@ -166,47 +145,3 @@ public class StoryDemoTest extends TestBase {
{"story_demo_get_response":{"msg":"Success","code":"10000","name":"白雪公主","id":1},"sign":"YMbxTPdovi6htcn1K3USTS6/Tbg6MOAMigG6x/kG0kQFCYH8ljvxXzcY86UT056nUG3OXxnj0xkw07eV6E03HMlu7bn3/jrT3PCcV3YguhA92aWz720x2xJWdfXY13OUPS9VOCC9zIVxu6EBD+PoZ7ojYChYvOfCR5I8bR/oOc0ZLjK63PWTBdf0eFS4sybXzRf81uNLMROsMhmBDDy0Fhml3ml77qzWBIpsmq5ECZ+89rMPbkNhAUcnFAe7ik7xZIL6WcUhAOhKVa8ZQK1GMjoGnAbGRed1FbuOHZGubgffg4/vMqrY10Bcy6h9jt/zK5w9L3HVgK3aPgQlfP16Gg=="}
```
## 开放现有接口
如果想把现有项目中的接口开放出去,提供给客户调用,具体操作如下:
- 将现有项目接入到SOP,前往`项目接入到SOP`文档页查看
- 在现有接口方法上加上一个注解`@ApiAbility`,如下面这个接口
```java
// 具备开放平台能力
@ApiAbility
@RequestMapping("getStory2")
public Story getStory2_0() {
Story story = new Story();
story.setId(1);
story.setName("海底小纵队(默认版本号)");
return story;
}
```
- 启动程序
这种情况下,老接口依然能正常访问,同时开放平台也能访问进来。
**注意** 此时的开放接口对应的接口名为:类@RequestMapping.value + "." + 方法@RequestMapping.value
举个列子:
```java
@RequestMapping("goods")
public class MyController {
@ApiAbility
@RequestMapping("listGoods")
public Object fun() {
}
}
```
fun接口对应的路径为:`/goods/listGoods`
那么对应开放平台的接口名会转换成:`goods.listGoods`,客户端的method参数要填`goods.listGoods`
当然也可以直接把@RequestMapping替换成`@ApiMapping`并指定接口名,这样的话不能兼容以前的访问形式。

@ -5,7 +5,8 @@
在参数中使用注解即可,框架会自动进行验证。如下面一个添加商品接口,它的参数是GoodsParam
```java
@ApiMapping(value = "goods.add")
@Open("goods.add")
@RequestMapping("/goods/add")
public void addGoods(GoodsParam param) {
...
}

@ -84,7 +84,7 @@ public class StoryResult {
* @return
*/
@ApiOperation(value = "获取故事信息", notes = "说明接口的详细信息,介绍,用途,注意事项等。")
@ApiMapping(value = "alipay.story.find")
@Open(value = "alipay.story.find")
public StoryResult getStory2(StoryParam story) {
log.info("获取故事信息参数, story: {}", story);
// 获取其它参数

@ -1,78 +0,0 @@
# easyopen支持
SOP对easyopen项目提供了很好的支持,如果您的服务端使用了easyopen框架,相关配置步骤如下:
## 服务端配置
首先是服务端相关配置
- pom添加依赖
```xml
<!-- sop接入依赖 -->
<dependency>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-service-common</artifactId>
<version>最新版本</version>
</dependency>
<!-- 使用nacos注册中心
版本 0.2.x.RELEASE 对应的是 Spring Boot 2.x 版本,版本 0.1.x.RELEASE 对应的是 Spring Boot 1.x 版本。
https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-alibaba-nacos-discovery
-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>0.2.2.RELEASE</version>
<exclusions>
<exclusion>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>net.oschina.durcframework</groupId>
<artifactId>easyopen</artifactId>
<version>1.16.1</version>
</dependency>
<!-- sop接入依赖 end -->
```
easyopen版本必须升级到1.16.1
- 启动类上面添加注解@EnableDiscoveryClient,将自己注册到注册中心
- 新增一个配置类,继承EasyopenServiceConfiguration,内容为空
```java
@Configuration
public class SopConfig extends EasyopenServiceConfiguration {
}
```
服务端配置完毕,重启服务。
## 网关端配置
接下来是网关的配置
- 打开ZuulConfig.java,注释掉原本的@Configuration,新增如下Configuration
```java
@Configuration
public class ZuulConfig extends EasyopenZuulConfiguration {
}
```
配置完毕,重启网关服务,可运行测试用例`EasyopenClientPostTest.java`验证
**注:** 配置完成后easyopen签名校验将会关闭,改用网关端来校验;网关对easyopen返回的结果不进行处理,直接返回服务端的结果。
完整配置可查看sop-example/sop-easyopen项目

@ -1,6 +1,6 @@
# 自定义校验token(2.5.0)
# 自定义校验token
从2.5.0开始在`@ApiMapping`注解中新增了一个属性`needToken`,用来告诉网关是否校验token
在`@Open`注解中有一个属性`needToken`,用来告诉网关是否校验token
```java
/**
@ -12,7 +12,9 @@ boolean needToken() default false;
使用方式:
```java
@ApiMapping(value = "story.token.get", needToken = true/* 设置true,网关会校验token是否存在 */)
@ApiOperation(value="传递token", notes = "传递token")
@Open(value = "story.get.token", needToken = true/* 设置true,网关会校验token是否存在 */)
@RequestMapping("token")
public StoryResult token(StoryParam story) {
OpenContext openContext = ServiceContext.getCurrentContext().getOpenContext();
String appAuthToken = openContext.getAppAuthToken();
@ -42,14 +44,11 @@ public class MyConfig {
ApiConfig.getInstance().setTokenValidator(apiParam -> {
// 获取客户端传递过来的token
String token = apiParam.fetchAccessToken();
if (StringUtils.isBlank(token)) {
return false;
}
return !StringUtils.isBlank(token);
// TODO: 校验token有效性,可以从redis中读取
// 返回true表示这个token真实、有效
return true;
});
});
}
}
```

@ -9,8 +9,8 @@
假设把路由a,b,c分配给了`VIP角色`,那么具有VIP角色的ISV可以访问a,b,c三个路由。
默认情况下,接口访问时公开的,ISV都能访问。如果要设置某个接口访问权限,在`@ApiMapping`注解中指定permission=true。
如:`@ApiMapping(value = "permission.story.get", permission = true)`。这样该接口是需要经过授权给ISV才能访问的。
默认情况下,接口访问时公开的,ISV都能访问。如果要设置某个接口访问权限,在`@Open`注解中指定permission=true。
如:`@Open(value = "permission.story.get", permission = true)`。这样该接口是需要经过授权给ISV才能访问的。
重启服务后,登录admin,服务管理-路由列表界面中,操作操作列会出现一个授权按钮,点击出现授权窗口,勾选对应的角色即可完成授权。

@ -1,16 +0,0 @@
# 使用SpringCloudGateway
修改`sop-gateway/pom.xml`配置,artifactId部分改成`sop-bridge-gateway`即可
```xml
<dependency>
<groupId>com.gitee.sop</groupId>
<!-- 使用zuul作为网关 -->
<!--<artifactId>sop-bridge-zuul</artifactId>-->
<!-- 使用spring cloud gateway作为网关 -->
<artifactId>sop-bridge-gateway</artifactId>
<version>对应版本</version>
</dependency>
```
修改完毕,重启sop-gateway

@ -1,27 +1,10 @@
# 传统web开发
# 提供rest接口
默认情况下SOP只提供开放接口,也可以同时提供restful接口,即程序提供一部分的开放接口,同时提供一部分restful接口。
默认情况下提供restful功能是关闭的,开启方式如下:
- 打开sop-gateway配置文件,新增一行配置:
```properties
# 提供restful接口
sop.restful.enable=true
```
- 前端app请求网关(`2.4.1之后有变动`)
有些接口没有被开放,但是也想要通过网关来访问,SOP提供一个固定的请求格式来访问。
请求格式:
**2.4.1版本之前:** `http://ip:port/rest/your_path`,其中`http://ip:port/rest/`为固定部分,后面跟微服务请求路径。
**2.4.1之后:** `http://ip:port/rest/服务id/your_path`,其中`http://ip:port/rest/`为固定部分,后面跟微服务请求路径。
**注意,`2.4.1`开始多了一个服务id作为区分,这样做是为了避免各微服务之间url冲突,假如两个微服务都有一个叫`/getItems`这样的接口
那么调用`http://ip:port/rest/getItems`接口网关无法做出正确的路由,虽然可以在代码上进行规范,为了防止万一,还是强行加上了,避免采坑
。可以指定`sop.restful.old-model=true`强制使用老的调用方式**
`http://ip:port/rest/服务id/your_path`,其中`http://ip:port/rest/`为固定部分,后面跟微服务请求路径。
> 可在微服务端指定一个配置:`sop.restful.prefix=xxx`。请求路径将变成:`http://ip:port/rest/xxx/your_path`
@ -48,7 +31,6 @@ public class TraditionalWebappController {
2. 本地访问:`http://10.0.1.22:2222/food/getFoodById/?id=2`
更多例子,可查看源码类:`TraditionalWebappController.java`
由此可见,对于前端调用者来说,它把网关看做一个大服务,只访问网关提供的请求,不需要关心网关后面的路由转发。网关后面各个微服务独自管理,
微服务之间的调用可以使用dubbo或feign,有了版本号的管理,可以做到服务的平滑升级,对用户来说都是无感知的。结合SOP-Admin提供的上下线功能,

@ -61,7 +61,8 @@ public UploadFile(String name, File file) throws IOException {
* @param param
* @return
*/
@ApiMapping(value = "demo.file.upload")
@Open("file.upload")
@RequestMapping("file1")
public FileUploadVO file1(FileUploadParam param) {
System.out.println(param.getRemark());
// 获取上传的文件
@ -95,7 +96,8 @@ public class FileUploadParam {
* @param param
* @return
*/
@ApiMapping(value = "demo.file.upload2")
@Open("file.upload2")
@RequestMapping("file2")
public FileUploadVO file2(FileUploadParam2 param, HttpServletRequest request) {
System.out.println(param.getRemark());
FileUploadVO vo = new FileUploadVO();

@ -21,8 +21,8 @@
```properties
# nacos cloud配置
#spring.cloud.nacos.discovery.server-addr=${nacos.url}
#nacos.config.server-addr=${nacos.url}
#spring.cloud.nacos.discovery.server-addr=${register.url}
#nacos.config.server-addr=${register.url}
```
添加eureka配置

@ -1,86 +0,0 @@
# 扩展其它注册中心
SOP默认使用的注册中心是[nacos](https://nacos.io/),可以扩展实现其它注册中心,其中`eureka`分支是已经扩展好的,使用eureka注册中心。
现在以扩展[consul](https://www.consul.io/)为例,说下具体扩展步骤:
- 扩展注册中心监听
在`sop-gateway-common`工程下,找到com.gitee.sop.gatewaycommon.route包,可以看到有两个类
`EurekaRegistryListener`和`NacosRegistryListener`
这两个类的作用是监听注册中心服务注册,从而触发事件,然后获取新注册的服务。
新建一个类:`ConsulRegistryListener`,继承`BaseRegistryListener`
实现onEvent方法,具体内容可参考`EurekaRegistryListener`类
```java
public class ConsulRegistryListener extends BaseRegistryListener {
/**
* 注册中心触发事件,可以从中获取服务<br>
*
* 这个方法做的事情有2个:<br>
*
* 1. 找出新注册的服务,调用pullRoutes方法<br>
* 2. 找出删除的服务,调用removeRoutes方法<br>
*
* @param applicationEvent 事件体
*/
@Override
public void onEvent(ApplicationEvent applicationEvent) {
}
}
```
配置类中新增:
```java
@Bean
@ConditionalOnProperty("spring.cloud.consul.host")
RegistryListener registryListenerConsul() {
return new ConsulRegistryListener();
}
```
其中`@ConditionalOnProperty("spring.cloud.consul.host")`的意思是只有配置了`spring.cloud.consul.host`属性,这个Bean才会被Spring注入
`sop-gateway`工程添加`Spring Cloud Consul`相关依赖,配置文件新增consul配置
- 扩展admin实现
找到`sop-admin-server`工程下com.gitee.sop.adminserver.service包,可以看到有两个类,`RegistryServiceEurekaImpl`和`RegistryServiceNacosImpl`
它们实现了`com.gitee.sop.adminserver.service.RegistryService`接口,因此我们要新建一个consul对应的类
新建`RegistryServiceConsulImpl`,然后实现RegistryService接口中的方法,具体可参考RegistryServiceEurekaImpl
```java
public class RegistryServiceConsulImpl implements RegistryService {
}
```
打开`com.gitee.sop.adminserver.config.WebConfig`类
新增一条配置
```java
/**
* 当配置了registry.name=eureka生效。
*
* @return
*/
@Bean
@ConditionalOnProperty(value = "registry.name", havingValue = "consul")
RegistryService registryServiceEureka() {
return new RegistryServiceConsulImpl();
}
```
application配置文件新增一条配置:
```properties
registry.name=consul
```

@ -1,21 +0,0 @@
# 原理分析之@ApiMapping注解
@ApiMapping注解的使用方式参考了Spring自带的@PostMapping注解
查看org.springframework.web.bind.annotation.PostMapping的类注释,有这么一句话:
> Specifically, @PostMapping is a composed annotation that acts as a shortcut for @RequestMapping(method = RequestMethod.POST).
翻译过来就是说,@PostMapping是一个组合模式的注解,可以看成是@RequestMapping(method = RequestMethod.POST)快捷方式。
如果我们自己定义个Mapping,仿照@PostMapping的方式,然后作用在方法上面会不会成功呢?实践证明是可以的。
@ApiMapping注解正是仿照了@PostMapping注解,然后再添加了几个自己的属性,比如版本号字段。
那么如何才能通过path + 版本号来确定一个接口呢?
springmvc提供了RequestCondition接口来实现这个功能,具体的操作可参考这篇文章:[让SpringMVC支持可版本管理的Restful接口](http://www.cnblogs.com/jcli/p/springmvc_restful_version.html)
SOP对应的是`com.gitee.sop.servercommon.mapping.ApiMappingRequestCondition`,这个类在com.gitee.sop.servercommon.mapping下。可以从`ApiMappingHandlerMapping`类开始解读。

@ -1,23 +0,0 @@
# 2.x升3.x注意事项
升级到3.x后`本地访问接口`方式会有不同。
```java
@ApiMapping(value = "alipay.story.get")
public StoryResult getStory(StoryParam param) {
StoryResult story = new StoryResult();
story.setId(1L);
story.setName("海底小纵队(alipay.story.get1.0), port:" + environment.getProperty("server.port") + ", param:" + param);
return story;
}
```
- 2.x版本访问方式:
`http://localhost:2222/alipay.story.get/?name=Jim&version=1.0`
- 3.x版本访问方式:
`http://localhost:2222/alipay.story.get/1.0/?name=Jim`
3.x版本中把版本号融合在了url中,如果这个功能没有用到,可以放心升级。

@ -34,7 +34,7 @@ Story bizObject = openContext.getBizObject(Story.class);
## 如何关闭签名验证
- 针对某一个接口关闭签名验证
`@ApiMapping(value = "alipay.story.get", ignoreValidate = true)`
`@Open(value = "alipay.story.get", ignoreValidate = true)`
- 针对所有接口关闭签名验证
@ -100,7 +100,7 @@ yml添加:
# https://blog.csdn.net/qq_36872046/article/details/81058045
# 路由转发超时时间,毫秒,默认值1000,详见:RibbonClientConfiguration.DEFAULT_READ_TIMEOUT。
# 如果微服务端 处理时间过长,会导致ribbon read超时,解决办法将这个值调大一点
ribbon.ReadTimeout: 60000
ribbon.ReadTimeout= 60000
```
## 指定了context-path(拉取路由404)

@ -51,7 +51,7 @@
<!-- Test -->
<junit.version>4.11</junit.version>
<fastjson.version>1.2.70</fastjson.version>
<fastjson.version>1.2.73</fastjson.version>
<commons-io.version>2.5</commons-io.version>
<commons-fileupload.version>1.3.3</commons-fileupload.version>
<commons-collection.version>3.2.2</commons-collection.version>

@ -22,8 +22,9 @@
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-bridge-nacos</artifactId>
<version>3.2.0-SNAPSHOT</version>
</dependency>
<!-- easyopen starter -->

@ -10,4 +10,13 @@
后台用户表:admin_user_info
密码保存规则:两次MD5,即`md5(md5("123456"))`
- 密码保存规则:`md5(username + md5(password) + username)`
```text
username: admin
password: 123456
md5("admin" + md5(123456) + "admin")
```
详见:com.gitee.sop.adminserver.AccountTest

@ -36,7 +36,7 @@ public class SystemApi {
String adminLogin(LoginForm param) {
String username = param.getUsername();
String password = param.getPassword();
password = DigestUtils.md5Hex(password);
password = DigestUtils.md5Hex(username + password + username);
Query query = new Query()
.eq("username", username)

@ -1,16 +1,29 @@
package com.gitee.sop.adminserver.bean;
import lombok.Data;
import java.util.List;
/**
* @author tanghc
*/
@Data
public class ServiceInfo {
/** 服务名称 */
private String serviceId;
/** 实例列表 */
private List<ServiceInstance> instances;
public String getServiceId() {
return serviceId;
}
public void setServiceId(String serviceId) {
this.serviceId = serviceId;
}
public List<ServiceInstance> getInstances() {
return instances;
}
public void setInstances(List<ServiceInstance> instances) {
this.instances = instances;
}
}

@ -10,10 +10,14 @@ admin.access-token.timeout-minutes=30
sop.sign-type=rsa
# nacos配置
nacos.config.server-addr=${nacos.url}
nacos.discovery.server-addr=${nacos.url}
nacos.config.server-addr=${register.url}
nacos.discovery.server-addr=${register.url}
# nacos cloud配置
spring.cloud.nacos.discovery.server-addr=${nacos.url}
spring.cloud.nacos.discovery.server-addr=${register.url}
# eureka地址
eureka.client.serviceUrl.defaultZone=${register.url}
# 数据库配置
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

@ -5,7 +5,7 @@ mysql.host=localhost:3306
mysql.username=root
mysql.password=root
# nacos注册中心地址
nacos.url=127.0.0.1:8848
# 注册中心地址
register.url=127.0.0.1:8848
logging.level.com.gitee=debug

@ -0,0 +1,23 @@
package com.gitee.sop.adminserver;
import junit.framework.TestCase;
import org.apache.commons.codec.digest.DigestUtils;
import org.junit.jupiter.api.Test;
/**
* @author tanghc
*/
public class AccountTest extends TestCase {
/*
生成密码
*/
@Test
public void genPwd() {
String username = "admin";
String password = "123456";
String save_to_db = DigestUtils.md5Hex(username + DigestUtils.md5Hex(password) + username);
System.out.println(save_to_db);
}
}

@ -1,6 +1,6 @@
package com.gitee.sop.sopauth.controller;
import com.gitee.sop.servercommon.annotation.ApiMapping;
import com.gitee.sop.servercommon.annotation.Open;
import com.gitee.sop.sopauth.auth.FetchTokenParam;
import com.gitee.sop.sopauth.auth.FetchTokenResult;
import com.gitee.sop.sopauth.auth.OAuth2Config;
@ -73,7 +73,8 @@ public class OAuth2Controller {
* @param param
* @return
*/
@ApiMapping("open.auth.token.app")
@Open("open.auth.token.app")
@RequestMapping("fetchToken")
@ResponseBody
public FetchTokenResult fetchToken(FetchTokenParam param) {
FetchTokenResult fetchTokenResult = oAuth2Service.accessToken(param, OAuth2Config.getInstance());

@ -8,12 +8,12 @@ mysql.username=root
mysql.password=root
# nacos地址
nacos.url=127.0.0.1:8848
register.url=127.0.0.1:8848
# ------- 需要改的配置end -------
# nacos cloud配置
spring.cloud.nacos.discovery.server-addr=${nacos.url}
spring.cloud.nacos.discovery.server-addr=${register.url}
# 数据库配置
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

@ -20,10 +20,10 @@
</properties>
<modules>
<module>sop-bridge-nacos</module>
<module>sop-bridge-eureka</module>
<module>sop-gateway-common</module>
<module>sop-service-common</module>
<module>sop-bridge-zuul</module>
<module>sop-bridge-gateway</module>
</modules>

@ -1,7 +1,7 @@
# sop-common
- sop-bridge-gateway:网关桥接器,供sop-gateway依赖,依赖后使用spring cloud gateway网关
- sop-bridge-zuul:网关桥接器,供sop-gateway依赖,依赖后使用spring cloud zuul网关
- sop-bridge-nacos:注册中心桥接器,接入nacos
- sop-bridge-eureka:注册中心桥接器,接入eureka
- sop-gateway-common:提供给网关使用
- sop-service-common:提供给微服务端使用,需要打成jar

@ -12,7 +12,7 @@
<modelVersion>4.0.0</modelVersion>
<version>3.2.0-SNAPSHOT</version>
<artifactId>sop-bridge-zuul</artifactId>
<artifactId>sop-bridge-eureka</artifactId>
<dependencies>
<dependency>
@ -23,11 +23,13 @@
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
</dependencies>
</project>

@ -0,0 +1,31 @@
package com.gitee.sop.bridge;
import com.gitee.sop.bridge.route.EurekaRegistryListener;
import com.gitee.sop.gatewaycommon.route.RegistryListener;
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
import org.springframework.cloud.netflix.ribbon.eureka.EurekaServerIntrospector;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author tanghc
*/
@Configuration
public class SopRegisterAutoConfiguration {
/**
* 负责获取eureka实例的metadata
* @return
*/
@Bean
ServerIntrospector eurekaServerIntrospector() {
return new EurekaServerIntrospector();
}
@Bean
RegistryListener registryListenerEureka() {
return new EurekaRegistryListener();
}
}

@ -1,6 +1,9 @@
package com.gitee.sop.gatewaycommon.route;
package com.gitee.sop.bridge.route;
import com.gitee.sop.gatewaycommon.bean.InstanceDefinition;
import com.gitee.sop.gatewaycommon.route.BaseRegistryListener;
import com.gitee.sop.gatewaycommon.route.RegistryEvent;
import com.gitee.sop.gatewaycommon.route.ServiceHolder;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.discovery.shared.Application;
import com.netflix.discovery.shared.Applications;

@ -11,7 +11,7 @@
<modelVersion>4.0.0</modelVersion>
<version>3.2.0-SNAPSHOT</version>
<artifactId>sop-bridge-gateway</artifactId>
<artifactId>sop-bridge-nacos</artifactId>
<dependencies>
<dependency>
@ -21,16 +21,14 @@
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

@ -0,0 +1,46 @@
package com.gitee.sop.bridge;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.discovery.NacosWatch;
import com.gitee.sop.bridge.route.NacosRegistryListener;
import com.gitee.sop.gatewaycommon.route.RegistryListener;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.scheduling.TaskScheduler;
import java.util.Map;
/**
* @author tanghc
*/
@Configuration
public class SopRegisterAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public NacosWatch nacosWatch(NacosDiscoveryProperties nacosDiscoveryProperties, ObjectProvider<TaskScheduler> taskScheduler, Environment environment) {
Map<String, String> metadata = nacosDiscoveryProperties.getMetadata();
String contextPath = environment.getProperty("server.servlet.context-path");
// 将context-path信息加入到metadata中
if (contextPath != null) {
metadata.put("context-path", contextPath);
}
// 在元数据中新增启动时间,不能修改这个值,不然网关拉取接口会有问题
metadata.put("time.startup", String.valueOf(System.currentTimeMillis()));
return new NacosWatch(nacosDiscoveryProperties, taskScheduler);
}
/**
* 微服务路由加载
*/
@Bean
RegistryListener registryListenerNacos() {
return new NacosRegistryListener();
}
}

@ -1,4 +1,4 @@
package com.gitee.sop.gatewaycommon.route;
package com.gitee.sop.bridge.route;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.nacos.api.exception.NacosException;
@ -6,6 +6,8 @@ import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.api.naming.pojo.ListView;
import com.gitee.sop.gatewaycommon.bean.InstanceDefinition;
import com.gitee.sop.gatewaycommon.route.BaseRegistryListener;
import com.gitee.sop.gatewaycommon.route.RegistryEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEvent;

@ -1,13 +1,14 @@
package com.gitee.sop.gatewaycommon.route;
package com.gitee.sop.bridge.route;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.gitee.sop.gatewaycommon.route.ServiceHolder;
/**
* @author tanghc
*/
public class NacosServiceHolder extends ServiceHolder {
private Instance instance;
private final Instance instance;
public NacosServiceHolder(String serviceId, long lastUpdatedTimestamp, Instance instance) {
super(serviceId, lastUpdatedTimestamp);

@ -7,7 +7,7 @@ sop.secret=MZZOUSTua6LzApIWXCwEgbBmxSzpzC
sop.gateway-index-path=/
# nacos cloud配置
spring.cloud.nacos.discovery.server-addr=${nacos.url}
spring.cloud.nacos.discovery.server-addr=${register.url}
# 数据库配置
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

@ -1,24 +0,0 @@
package com.gitee.sop.bridge;
import com.gitee.sop.gatewaycommon.config.BaseGatewayAutoConfiguration;
import com.gitee.sop.gatewaycommon.zuul.configuration.AlipayZuulConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* https://blog.csdn.net/seashouwang/article/details/80299571
* @author tanghc
*/
@Configuration
@EnableZuulProxy
@Import(AlipayZuulConfiguration.class)
// 在ErrorMvcAutoConfiguration之前加载
// 如果不加会出现basicErrorController和zuulErrorController冲突
// zuulErrorController是SOP中的,提前加载后basicErrorController就不会加载
@AutoConfigureBefore({ErrorMvcAutoConfiguration.class})
public class SopGatewayAutoConfiguration extends BaseGatewayAutoConfiguration {
}

@ -19,6 +19,7 @@
</properties>
<dependencies>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
@ -60,12 +61,6 @@
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
@ -76,11 +71,6 @@
<artifactId>spring-boot-starter-webflux</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-eureka-server</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>

@ -25,7 +25,6 @@ import com.gitee.sop.gatewaycommon.result.DataNameBuilder;
import com.gitee.sop.gatewaycommon.result.DefaultDataNameBuilder;
import com.gitee.sop.gatewaycommon.result.ResultAppender;
import com.gitee.sop.gatewaycommon.result.ResultExecutorForGateway;
import com.gitee.sop.gatewaycommon.result.ResultExecutorForZuul;
import com.gitee.sop.gatewaycommon.secret.CacheIsvManager;
import com.gitee.sop.gatewaycommon.secret.IsvManager;
import com.gitee.sop.gatewaycommon.session.ApiSessionManager;
@ -37,9 +36,6 @@ import com.gitee.sop.gatewaycommon.validate.Encrypter;
import com.gitee.sop.gatewaycommon.validate.Signer;
import com.gitee.sop.gatewaycommon.validate.TokenValidator;
import com.gitee.sop.gatewaycommon.validate.Validator;
import com.gitee.sop.gatewaycommon.zuul.controller.ZuulErrorController;
import com.gitee.sop.gatewaycommon.zuul.param.ZuulParamBuilder;
import com.gitee.sop.gatewaycommon.zuul.result.ZuulResultExecutor;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
@ -67,11 +63,6 @@ public class ApiConfig {
*/
private ResultExecutorForGateway gatewayResultExecutor = new GatewayResultExecutor();
/**
* zuul合并结果处理
*/
private ResultExecutorForZuul zuulResultExecutor = new ZuulResultExecutor();
/**
* isv管理
*/
@ -87,11 +78,6 @@ public class ApiConfig {
*/
private Signer signer = new ApiSigner();
/**
* 参数解析zuul
*/
private ZuulParamBuilder zuulParamBuilder = new ZuulParamBuilder();
/**
* 验证
*/
@ -147,11 +133,6 @@ public class ApiConfig {
*/
private ServiceErrorManager serviceErrorManager = new DefaultServiceErrorManager();
/**
* zuul网关全局异常处理
*/
private ZuulErrorController zuulErrorController = new ZuulErrorController();
private ParameterFormatter parameterFormatter;
/**

@ -52,5 +52,6 @@ public class SopConstants {
public static final String CACHE_ROUTE_INTERCEPTOR_CONTEXT = "cacheRouteInterceptorContext";
public static final String TARGET_SERVICE = "sop-target-service";
public static final String RESTFUL_REQUEST = "sop-restful-request";
}

@ -1,4 +1,4 @@
package com.gitee.sop.gatewaycommon.manager;
package com.gitee.sop.gatewaycommon.config;
import com.gitee.sop.gatewaycommon.bean.ApiConfig;
import com.gitee.sop.gatewaycommon.bean.ApiContext;
@ -8,11 +8,16 @@ import com.gitee.sop.gatewaycommon.gateway.loadbalancer.NacosServerIntrospector;
import com.gitee.sop.gatewaycommon.interceptor.MonitorRouteInterceptor;
import com.gitee.sop.gatewaycommon.interceptor.RouteInterceptor;
import com.gitee.sop.gatewaycommon.limit.LimitManager;
import com.gitee.sop.gatewaycommon.loadbalancer.SopPropertiesFactory;
import com.gitee.sop.gatewaycommon.manager.EnvGrayManager;
import com.gitee.sop.gatewaycommon.manager.EnvironmentContext;
import com.gitee.sop.gatewaycommon.manager.EnvironmentKeys;
import com.gitee.sop.gatewaycommon.manager.IPBlacklistManager;
import com.gitee.sop.gatewaycommon.manager.IsvRoutePermissionManager;
import com.gitee.sop.gatewaycommon.manager.LimitConfigManager;
import com.gitee.sop.gatewaycommon.manager.RouteConfigManager;
import com.gitee.sop.gatewaycommon.manager.RouteRepositoryContext;
import com.gitee.sop.gatewaycommon.message.ErrorFactory;
import com.gitee.sop.gatewaycommon.param.ParameterFormatter;
import com.gitee.sop.gatewaycommon.route.EurekaRegistryListener;
import com.gitee.sop.gatewaycommon.route.NacosRegistryListener;
import com.gitee.sop.gatewaycommon.route.RegistryListener;
import com.gitee.sop.gatewaycommon.route.ServiceListener;
import com.gitee.sop.gatewaycommon.route.ServiceRouteListener;
@ -29,9 +34,7 @@ import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.client.discovery.event.HeartbeatEvent;
import org.springframework.cloud.netflix.ribbon.PropertiesFactory;
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
import org.springframework.cloud.netflix.ribbon.eureka.EurekaServerIntrospector;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEvent;
@ -39,9 +42,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.event.EventListener;
import org.springframework.core.env.Environment;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.filter.CorsFilter;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
@ -96,27 +97,6 @@ public class AbstractConfiguration implements ApplicationContextAware, Applicati
registryListener.onEvent(heartbeatEvent);
}
@Bean
@ConditionalOnProperty("zuul.servlet-path")
PropertiesFactory propertiesFactory() {
return new SopPropertiesFactory();
}
/**
* 微服务路由加载
*/
@Bean
@ConditionalOnProperty("spring.cloud.nacos.discovery.server-addr")
RegistryListener registryListenerNacos() {
return new NacosRegistryListener();
}
@Bean
@ConditionalOnProperty("eureka.client.serviceUrl.defaultZone")
RegistryListener registryListenerEureka() {
return new EurekaRegistryListener();
}
@Bean
@ConditionalOnMissingBean
ServiceListener serviceListener() {
@ -184,18 +164,6 @@ public class AbstractConfiguration implements ApplicationContextAware, Applicati
}
/**
* 跨域过滤器zuul
*/
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty("zuul.servlet-path")
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", createCorsConfiguration());
return new CorsFilter(source);
}
/**
* 跨域过滤器gateway采用react形式需要使用reactive包下的UrlBasedCorsConfigurationSource
*/
@ -226,15 +194,7 @@ public class AbstractConfiguration implements ApplicationContextAware, Applicati
return new NacosServerIntrospector();
}
/**
* 负责获取eureka实例的metadata
* @return
*/
@Bean
@ConditionalOnProperty("eureka.client.serviceUrl.defaultZone")
ServerIntrospector eurekaServerIntrospector() {
return new EurekaServerIntrospector();
}
@Override
public void run(ApplicationArguments args) throws Exception {

@ -1,6 +1,5 @@
package com.gitee.sop.bridge;
package com.gitee.sop.gatewaycommon.config;
import com.gitee.sop.gatewaycommon.config.BaseGatewayAutoConfiguration;
import com.gitee.sop.gatewaycommon.gateway.configuration.AlipayGatewayConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;

@ -20,7 +20,7 @@ public class SopGatewayEnvironmentPostProcessor implements EnvironmentPostProces
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
Resource resource = new ClassPathResource("sop-bridge.properties");
Resource resource = new ClassPathResource("META-INF/gateway.properties");
// 加载成PropertySource对象,并添加到Environment环境中
environment.getPropertySources().addLast(loadProfiles(resource));
}

@ -1,44 +0,0 @@
package com.gitee.sop.gatewaycommon.easyopen;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.gitee.sop.gatewaycommon.message.Error;
import com.gitee.sop.gatewaycommon.result.ApiResult;
import com.gitee.sop.gatewaycommon.result.ResultExecutorForZuul;
import com.gitee.sop.gatewaycommon.zuul.result.ZuulResultExecutor;
import com.netflix.zuul.context.RequestContext;
import java.util.Locale;
import java.util.Optional;
/**
* @author tanghc
*/
public class EasyopenResultExecutor implements ResultExecutorForZuul {
boolean onlyReturnData;
public EasyopenResultExecutor(boolean onlyReturnData) {
this.onlyReturnData = onlyReturnData;
}
@Override
public String mergeResult(RequestContext request, String serviceResult) {
if (onlyReturnData) {
JSONObject jsonObject = JSON.parseObject(serviceResult);
return Optional.ofNullable(jsonObject.getString("data")).orElse("{}");
} else {
return serviceResult;
}
}
@Override
public String buildErrorResult(RequestContext requestContext, Throwable ex) {
ApiResult apiResult = new ApiResult();
Locale locale = requestContext.getRequest().getLocale();
Error error = ZuulResultExecutor.getError(locale, ex);
apiResult.setCode(error.getSub_code());
apiResult.setMsg(error.getSub_msg());
return JSON.toJSONString(apiResult);
}
}

@ -1,33 +0,0 @@
package com.gitee.sop.gatewaycommon.easyopen;
import com.gitee.sop.gatewaycommon.param.ApiParam;
import com.gitee.sop.gatewaycommon.validate.AbstractSigner;
import org.apache.commons.codec.digest.DigestUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
/**
* @author tanghc
*/
public class EasyopenSigner extends AbstractSigner {
@Override
protected String buildServerSign(ApiParam params, String secret) {
Set<String> keySet = params.keySet();
List<String> paramNames = new ArrayList<>(keySet);
Collections.sort(paramNames);
StringBuilder paramNameValue = new StringBuilder();
for (String paramName : paramNames) {
paramNameValue.append(paramName).append(params.get(paramName));
}
String source = secret + paramNameValue.toString() + secret;
return DigestUtils.md5Hex(source).toUpperCase();
}
}

@ -1,38 +0,0 @@
package com.gitee.sop.gatewaycommon.easyopen;
import com.gitee.sop.gatewaycommon.bean.ApiConfig;
import com.gitee.sop.gatewaycommon.bean.ApiContext;
import com.gitee.sop.gatewaycommon.param.ParamNames;
import com.gitee.sop.gatewaycommon.zuul.configuration.BaseZuulConfiguration;
/**
* @author tanghc
*/
public class EasyopenZuulConfiguration extends BaseZuulConfiguration {
public EasyopenZuulConfiguration() {
ApiConfig apiConfig = ApiContext.getApiConfig();
if (compatibilityModel()) {
ParamNames.APP_KEY_NAME = "app_key";
ParamNames.API_NAME = "name";
ParamNames.SIGN_TYPE_NAME = "sign_type";
ParamNames.APP_AUTH_TOKEN_NAME = "access_token";
apiConfig.setSigner(new EasyopenSigner());
apiConfig.setZuulResultExecutor(new EasyopenResultExecutor(false));
apiConfig.setMergeResult(false);
} else {
apiConfig.setZuulResultExecutor(new EasyopenResultExecutor(true));
}
}
/**
* 是否是兼容模式
* @return 返回true返回true可兼容之前的easyopen接口
*/
public boolean compatibilityModel() {
return true;
}
}

@ -81,13 +81,10 @@ public class ServerWebExchangeUtil {
return ServerRequest.create(exchange, messageReaders);
}
public static ApiParam getApiParamForRestful(ServerWebExchange exchange, String path) {
public static String getRestfulPath(String path) {
int index = path.indexOf(REST_PATH);
// 取"/rest"的后面部分
String newPath = path.substring(index + REST_PATH.length());
ApiParam apiParam = ApiParam.createRestfulApiParam(newPath);
setApiParam(exchange, apiParam);
return apiParam;
return path.substring(index + REST_PATH.length());
}
/**
@ -97,7 +94,7 @@ public class ServerWebExchangeUtil {
* @param forwardPath 重定向path
* @return 返回新的ServerWebExchange配合chain.filter(newExchange);使用
*/
private static ServerWebExchange getForwardExchange(ServerWebExchange exchange, String forwardPath) {
public static ServerWebExchange getForwardExchange(ServerWebExchange exchange, String forwardPath) {
ServerHttpRequest newRequest = exchange.getRequest()
.mutate()
.path(forwardPath).build();

@ -15,7 +15,7 @@ import com.gitee.sop.gatewaycommon.gateway.loadbalancer.SopLoadBalancerClient;
import com.gitee.sop.gatewaycommon.gateway.route.GatewayForwardChooser;
import com.gitee.sop.gatewaycommon.gateway.route.GatewayRouteCache;
import com.gitee.sop.gatewaycommon.gateway.route.GatewayRouteRepository;
import com.gitee.sop.gatewaycommon.manager.AbstractConfiguration;
import com.gitee.sop.gatewaycommon.config.AbstractConfiguration;
import com.gitee.sop.gatewaycommon.manager.RouteRepositoryContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectProvider;

@ -1,6 +1,7 @@
package com.gitee.sop.gatewaycommon.gateway.filter;
import com.gitee.sop.gatewaycommon.bean.ApiContext;
import com.gitee.sop.gatewaycommon.bean.SopConstants;
import com.gitee.sop.gatewaycommon.result.ResultExecutor;
import org.apache.commons.lang3.StringUtils;
import org.reactivestreams.Publisher;
@ -48,6 +49,10 @@ public class GatewayModifyResponseGatewayFilter implements GlobalFilter, Ordered
if (StringUtils.containsIgnoreCase(originalResponseContentType, MediaType.APPLICATION_OCTET_STREAM_VALUE)) {
return chain.filter(exchange);
}
// rest请求,直接放行
if (exchange.getAttribute(SopConstants.RESTFUL_REQUEST) != null) {
return chain.filter(exchange);
}
Class inClass = String.class;
Class outClass = String.class;
HttpHeaders httpHeaders = new HttpHeaders();

@ -48,7 +48,7 @@ public class IndexFilter implements WebFilter {
private static final String REST_PATH_PREFIX = "/rest";
/** 路径白名单 */
private static List<String> PATH_WHITE_LIST = Arrays.asList(
private static final List<String> PATH_WHITE_LIST = Arrays.asList(
"/sop", "/actuator"
);
@ -71,15 +71,9 @@ public class IndexFilter implements WebFilter {
}
// 如果是restful请求,直接转发
if (path.startsWith(REST_PATH_PREFIX)) {
String sopRestfulEnableValue = EnvironmentKeys.SOP_RESTFUL_ENABLE.getValue();
if (!Objects.equals("true", sopRestfulEnableValue)) {
log.error("尝试调用restful请求,但sop.restful.enable未开启");
return ServerWebExchangeUtil.forwardUnknown(exchange, chain);
}
ApiParam apiParam = ServerWebExchangeUtil.getApiParamForRestful(exchange, path);
this.doValidate(exchange, apiParam);
ForwardInfo forwardInfo = gatewayForwardChooser.getForwardInfo(exchange);
ServerWebExchange newExchange = ServerWebExchangeUtil.getForwardExchange(exchange, forwardInfo);
exchange.getAttributes().put(SopConstants.RESTFUL_REQUEST, true);
String restfulPath = ServerWebExchangeUtil.getRestfulPath(path);
ServerWebExchange newExchange = ServerWebExchangeUtil.getForwardExchange(exchange, restfulPath);
return chain.filter(newExchange);
}
if (Objects.equals(path, indexPath)) {

@ -2,6 +2,7 @@ package com.gitee.sop.gatewaycommon.gateway.filter;
import com.gitee.sop.gatewaycommon.bean.ApiConfig;
import com.gitee.sop.gatewaycommon.bean.ConfigLimitDto;
import com.gitee.sop.gatewaycommon.bean.SopConstants;
import com.gitee.sop.gatewaycommon.exception.ApiException;
import com.gitee.sop.gatewaycommon.gateway.ServerWebExchangeUtil;
import com.gitee.sop.gatewaycommon.limit.LimitManager;
@ -32,12 +33,18 @@ public class LimitFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
if (exchange.getAttribute(SopConstants.RESTFUL_REQUEST) != null) {
return chain.filter(exchange);
}
ApiConfig apiConfig = ApiConfig.getInstance();
// 限流功能未开启,直接返回
if (!apiConfig.isOpenLimit()) {
return chain.filter(exchange);
}
ApiParam apiParam = ServerWebExchangeUtil.getApiParam(exchange);
if (apiParam == null) {
return chain.filter(exchange);
}
ConfigLimitDto configLimitDto = this.findConfigLimitDto(apiConfig, apiParam, exchange);
if (configLimitDto == null) {
return chain.filter(exchange);

@ -3,10 +3,10 @@ package com.gitee.sop.gatewaycommon.gateway.result;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.gitee.sop.gatewaycommon.bean.DefaultRouteInterceptorContext;
import com.gitee.sop.gatewaycommon.interceptor.RouteInterceptorContext;
import com.gitee.sop.gatewaycommon.bean.SopConstants;
import com.gitee.sop.gatewaycommon.exception.ApiException;
import com.gitee.sop.gatewaycommon.gateway.ServerWebExchangeUtil;
import com.gitee.sop.gatewaycommon.interceptor.RouteInterceptorContext;
import com.gitee.sop.gatewaycommon.message.Error;
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
import com.gitee.sop.gatewaycommon.param.ApiParam;

@ -1,27 +0,0 @@
package com.gitee.sop.gatewaycommon.loadbalancer;
import com.gitee.sop.gatewaycommon.manager.EnvironmentKeys;
import com.netflix.loadbalancer.IRule;
import org.springframework.cloud.netflix.ribbon.PropertiesFactory;
/**
* 自定义PropertiesFactory用来动态添加LoadBalance规则
* @author tanghc
*/
public class SopPropertiesFactory extends PropertiesFactory {
/**
* 配置文件配置<serviceId>.ribbon.NFLoadBalancerRuleClassName=com.gitee.sop.gateway.loadbalancer.EnvironmentServerChooser
* @param clazz
* @param name serviceId
* @return 返回class全限定名
*/
@Override
public String getClassName(Class clazz, String name) {
if (clazz == IRule.class) {
return EnvironmentKeys.ZUUL_CUSTOM_RULE_CLASSNAME.getValue();
} else {
return super.getClassName(clazz, name);
}
}
}

@ -1,6 +1,6 @@
package com.gitee.sop.gatewaycommon.manager;
import com.gitee.sop.gatewaycommon.zuul.loadbalancer.ServiceGrayConfig;
import com.gitee.sop.gatewaycommon.loadbalancer.ServiceGrayConfig;
import com.google.common.collect.Maps;
import java.util.Map;
@ -13,12 +13,12 @@ public class DefaultEnvGrayManager implements EnvGrayManager {
/**
* keyserviceId服务对应的灰度配置
*/
private Map<String, ServiceGrayConfig> serviceGrayConfigMap = Maps.newConcurrentMap();
private final Map<String, ServiceGrayConfig> serviceGrayConfigMap = Maps.newConcurrentMap();
/**
* key:instanceId value:serviceId
*/
private Map<String, String> instanceIdServiceIdMap = Maps.newConcurrentMap();
private final Map<String, String> instanceIdServiceIdMap = Maps.newConcurrentMap();
@Override
public void saveServiceGrayConfig(ServiceGrayConfig serviceGrayConfig) {

@ -1,7 +1,7 @@
package com.gitee.sop.gatewaycommon.manager;
import com.gitee.sop.gatewaycommon.bean.BeanInitializer;
import com.gitee.sop.gatewaycommon.zuul.loadbalancer.ServiceGrayConfig;
import com.gitee.sop.gatewaycommon.loadbalancer.ServiceGrayConfig;
/**
* @author tanghc

@ -1,28 +1,17 @@
package com.gitee.sop.gatewaycommon.manager;
import com.gitee.sop.gatewaycommon.zuul.loadbalancer.EnvironmentServerChooser;
public enum EnvironmentKeys {
SPRING_PROFILES_ACTIVE("spring.profiles.active", "default"),
/**
* spring.application.name
*/
SPRING_APPLICATION_NAME("spring.application.name"),
/**
* 指定负载均衡规则类
*/
ZUUL_CUSTOM_RULE_CLASSNAME("zuul.custom-rule-classname", EnvironmentServerChooser.class.getName()),
/**
* sign.urlencode=true签名验证拼接字符串的value部分进行urlencode
*/
SIGN_URLENCODE("sign.urlencode"),
/**
* sop.restful.enable=true开启传统web开发模式
*/
SOP_RESTFUL_ENABLE("sop.restful.enable"),
/**
* sop.restful.path=/xx 指定请求前缀默认/rest
*/
@ -39,9 +28,11 @@ public enum EnvironmentKeys {
/**
* 预发布域名
*/
PRE_DOMAIN("pre.domain");
PRE_DOMAIN("pre.domain"),
;
private String key;
private final String key;
private String defaultValue;
public String getKey() {

@ -1,9 +0,0 @@
package com.gitee.sop.gatewaycommon.result;
import com.netflix.zuul.context.RequestContext;
/**
* @author tanghc
*/
public interface ResultExecutorForZuul extends ResultExecutor<RequestContext, String> {
}

@ -3,9 +3,7 @@ package com.gitee.sop.gatewaycommon.route;
import com.alibaba.fastjson.JSON;
import com.gitee.sop.gatewaycommon.bean.InstanceDefinition;
import com.gitee.sop.gatewaycommon.bean.ServiceRouteInfo;
import com.gitee.sop.gatewaycommon.bean.SopConstants;
import com.gitee.sop.gatewaycommon.manager.BaseRouteCache;
import com.gitee.sop.gatewaycommon.manager.EnvironmentKeys;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
@ -46,7 +44,7 @@ public class ServiceRouteListener extends BaseServiceListener {
String serviceName = instance.getServiceId();
String url = getRouteRequestUrl(instance);
log.info("拉取路由配置,serviceId: {}, url: {}", serviceName, url);
ResponseEntity<String> responseEntity = getRestTemplate().postForEntity(url, getHttpEntity(), String.class);
ResponseEntity<String> responseEntity = getRestTemplate().getForEntity(url, String.class);
if (responseEntity.getStatusCode() == HttpStatus.OK) {
String body = responseEntity.getBody();
ServiceRouteInfo serviceRouteInfo = JSON.parseObject(body, ServiceRouteInfo.class);
@ -58,9 +56,6 @@ public class ServiceRouteListener extends BaseServiceListener {
protected HttpEntity<String> getHttpEntity() {
HttpHeaders headers = new HttpHeaders();
String restful = EnvironmentKeys.SOP_RESTFUL_ENABLE.getValue();
boolean enableRestful = "true".equals(restful);
headers.add(HEADER_RESTFUL, String.valueOf(enableRestful));
return new HttpEntity<>(headers);
}

@ -4,8 +4,6 @@ import com.alibaba.fastjson.JSON;
import com.gitee.sop.gatewaycommon.bean.SopConstants;
import com.gitee.sop.gatewaycommon.param.ApiUploadContext;
import com.gitee.sop.gatewaycommon.param.UploadContext;
import com.gitee.sop.gatewaycommon.zuul.param.ZuulParameterUtil;
import com.netflix.zuul.http.HttpServletRequestWrapper;
import lombok.Data;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.fileupload.FileItem;
@ -16,8 +14,6 @@ import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
@ -121,9 +117,6 @@ public class RequestUtil {
* @return 返回参数键值对
*/
public static Map<String, String> convertRequestParamsToMap(HttpServletRequest request) {
if (request instanceof HttpServletRequestWrapper) {
request = ((HttpServletRequestWrapper) request).getRequest();
}
Map<String, String[]> paramMap = request.getParameterMap();
if (paramMap == null || paramMap.isEmpty()) {
return Collections.emptyMap();
@ -342,26 +335,6 @@ public class RequestUtil {
}
}
public static HttpServletRequest wrapRequest(HttpServletRequest request) {
if (request.getMethod().equalsIgnoreCase(HttpMethod.GET.name()) ||
request instanceof StandardMultipartHttpServletRequest) {
return request;
}
HttpServletRequest wrapper = request;
String contentType = request.getContentType();
MediaType mediaType = MediaType.valueOf(contentType);
if (MediaType.APPLICATION_JSON.includes(mediaType)) {
try {
String json = RequestUtil.getText(request);
byte[] data = json.getBytes(StandardCharsets.UTF_8);
wrapper = new ZuulParameterUtil.BodyDataHttpServletRequestWrapper(request, data);
} catch (IOException e) {
log.error("wrapRequest异常", e);
}
}
return wrapper;
}
@Data
public static class UploadInfo {
private Map<String, String> uploadParams;

@ -1,37 +0,0 @@
package com.gitee.sop.gatewaycommon.zuul;
import com.netflix.util.Pair;
import com.netflix.zuul.context.RequestContext;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpHeaders;
import java.util.List;
/**
* @author tanghc
*/
public class RequestContextUtil {
/**
* 获取微服务端传递过来的header
*
* @param name header名
* @return 返回value没有返回null
*/
public static String getZuulResponseHeader(RequestContext requestContext, String name) {
List<Pair<String, String>> zuulResponseHeaders = requestContext.getZuulResponseHeaders();
return zuulResponseHeaders.stream()
.filter(pair -> StringUtils.containsIgnoreCase(pair.first(), name))
.findFirst()
.map(Pair::second)
.orElse(null);
}
/**
* 获取微服务端的content-type
* @return 返回content-type
*/
public static String getZuulContentType(RequestContext requestContext) {
return getZuulResponseHeader(requestContext, HttpHeaders.CONTENT_TYPE);
}
}

@ -1,97 +0,0 @@
package com.gitee.sop.gatewaycommon.zuul;
import com.gitee.sop.gatewaycommon.bean.ApiConfig;
import com.gitee.sop.gatewaycommon.bean.DefaultRouteInterceptorContext;
import com.gitee.sop.gatewaycommon.bean.SopConstants;
import com.gitee.sop.gatewaycommon.param.ApiParam;
import com.gitee.sop.gatewaycommon.param.ParamBuilder;
import com.gitee.sop.gatewaycommon.util.ResponseUtil;
import com.gitee.sop.gatewaycommon.util.RouteInterceptorUtil;
import com.gitee.sop.gatewaycommon.validate.Validator;
import com.netflix.zuul.context.RequestContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
/**
* 负责签名校验
* @author tanghc
*/
@Slf4j
public class ValidateService {
@Autowired
private ParamBuilder<RequestContext> paramBuilder;
@Autowired
private Validator validator;
/**
* 校验操作
*
* @param currentContext currentContext
* @param callback 校验后操作
*/
public void validate(RequestContext currentContext, ValidateCallback callback) {
// 解析参数
ApiParam param = ZuulContext.getApiParam();
if (param == null) {
param = paramBuilder.build(currentContext);
ZuulContext.setApiParam(param);
}
doValidate(currentContext, param, callback);
}
/**
* 签名校验
*
* @param currentContext currentContext
*/
private void doValidate(RequestContext currentContext, ApiParam param, ValidateCallback callback) {
Exception error = null;
// 验证操作,这里有负责验证签名参数
try {
validator.validate(param);
this.afterValidate(currentContext, param);
} catch (Exception e) {
error = e;
}
param.fitNameVersion();
if (callback != null) {
if (error == null) {
callback.onSuccess(currentContext);
} else {
callback.onError(currentContext, param, error);
}
}
}
private void afterValidate(RequestContext currentContext, ApiParam param) {
RouteInterceptorUtil.runPreRoute(currentContext, param, context -> {
DefaultRouteInterceptorContext defaultRouteInterceptorContext = (DefaultRouteInterceptorContext) context;
defaultRouteInterceptorContext.setRequestDataSize(currentContext.getRequest().getContentLengthLong());
currentContext.set(SopConstants.CACHE_ROUTE_INTERCEPTOR_CONTEXT, context);
});
}
public interface ValidateCallback {
/**
* 校验成功触发
*
* @param currentContext 上下文
*/
void onSuccess(RequestContext currentContext);
/**
* 校验失败触发
*
* @param currentContext 上下文
* @param param 参数
* @param throwable 异常
*/
default void onError(RequestContext currentContext, ApiParam param, Throwable throwable) {
log.error("验证失败,ip:{}, params:{}, errorMsg:{}", param.fetchIp(), param.toJSONString(), throwable.getMessage());
String errorResult = ApiConfig.getInstance().getZuulResultExecutor().buildErrorResult(currentContext, throwable);
ResponseUtil.writeJson(currentContext.getResponse(), errorResult);
}
}
}

@ -1,137 +0,0 @@
package com.gitee.sop.gatewaycommon.zuul;
import com.gitee.sop.gatewaycommon.bean.ApiConfig;
import com.gitee.sop.gatewaycommon.bean.ApiContext;
import com.gitee.sop.gatewaycommon.param.ApiParam;
import com.netflix.zuul.context.RequestContext;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.Locale;
/**
* @author tanghc
*/
public class ZuulContext extends ApiContext {
private static final String ATTR_PARAM = "zuul.common.api.param";
private static void setAttr(String name, Object val) {
HttpServletRequest request = getRequest();
if (request != null) {
request.setAttribute(name, val);
}
}
private static Object getAttr(String name) {
HttpServletRequest request = getRequest();
if (request == null) {
return null;
}
return request.getAttribute(name);
}
/**
* 获取HttpServletRequest
*
* @return HttpServletRequest
*/
public static HttpServletRequest getRequest() {
return RequestContext.getCurrentContext().getRequest();
}
/**
* 返回默认的HttpServletRequest.getSession();
*
* @return 没有返回null
*/
public static HttpSession getSession() {
HttpServletRequest req = getRequest();
if (req == null) {
return null;
} else {
return req.getSession();
}
}
/**
* 同getSessionId()
*
* @return 返回accessToken, 没有返回null
*/
public static String getAccessToken() {
return getSessionId();
}
/**
* 获取登陆的token
*
* @return 没有返回null
*/
public static String getSessionId() {
ApiParam apiParam = getApiParam();
if (apiParam == null) {
return null;
}
return apiParam.fetchAccessToken();
}
/**
* 获取本地化从HttpServletRequest中获取没有则返回Locale.SIMPLIFIED_CHINESE
*
* @return Locale
*/
public static Locale getLocale() {
HttpServletRequest req = getRequest();
if (req == null) {
return Locale.SIMPLIFIED_CHINESE;
}
return req.getLocale();
}
public static void setApiParam(ApiParam apiParam) {
setAttr(ATTR_PARAM, apiParam);
}
/**
* 获取系统参数
*
* @return 返回ApiParam
*/
public static ApiParam getApiParam() {
return (ApiParam) getAttr(ATTR_PARAM);
}
public static ApiConfig getApiConfig() {
return ApiConfig.getInstance();
}
public static void setApiConfig(ApiConfig apiConfig) {
ApiConfig.setInstance(apiConfig);
}
public static ServletContext getServletContext() {
ServletContext ctx = null;
HttpSession session = getSession();
if (session != null) {
ctx = session.getServletContext();
}
return ctx;
}
/**
* 获取response
*
* @return 返回response
*/
public static HttpServletResponse getResponse() {
return RequestContext.getCurrentContext().getResponse();
}
}

@ -1,16 +0,0 @@
package com.gitee.sop.gatewaycommon.zuul.configuration;
import com.gitee.sop.gatewaycommon.bean.ApiContext;
import com.gitee.sop.gatewaycommon.validate.alipay.AlipaySigner;
/**
* 具备支付宝开放平台能力配置 https://docs.open.alipay.com/api
*
* @author tanghc
*/
public class AlipayZuulConfiguration extends BaseZuulConfiguration {
static {
ApiContext.getApiConfig().setSigner(new AlipaySigner());
}
}

@ -1,176 +0,0 @@
package com.gitee.sop.gatewaycommon.zuul.configuration;
import com.gitee.sop.gatewaycommon.bean.ApiConfig;
import com.gitee.sop.gatewaycommon.bean.ApiContext;
import com.gitee.sop.gatewaycommon.manager.AbstractConfiguration;
import com.gitee.sop.gatewaycommon.manager.RouteRepositoryContext;
import com.gitee.sop.gatewaycommon.param.ParamBuilder;
import com.gitee.sop.gatewaycommon.zuul.ValidateService;
import com.gitee.sop.gatewaycommon.zuul.controller.ConfigChannelController;
import com.gitee.sop.gatewaycommon.zuul.controller.ErrorLogController;
import com.gitee.sop.gatewaycommon.zuul.controller.ZuulErrorController;
import com.gitee.sop.gatewaycommon.zuul.controller.ZuulIndexController;
import com.gitee.sop.gatewaycommon.zuul.controller.ZuulMonitorController;
import com.gitee.sop.gatewaycommon.zuul.filter.ErrorFilter;
import com.gitee.sop.gatewaycommon.zuul.filter.FormBodyWrapperFilterExt;
import com.gitee.sop.gatewaycommon.zuul.filter.PostResultFilter;
import com.gitee.sop.gatewaycommon.zuul.filter.PreHttpServletRequestWrapperFilter;
import com.gitee.sop.gatewaycommon.zuul.filter.PreLimitFilter;
import com.gitee.sop.gatewaycommon.zuul.filter.PreParameterFormatterFilter;
import com.gitee.sop.gatewaycommon.zuul.filter.Servlet30WrapperFilterExt;
import com.gitee.sop.gatewaycommon.zuul.route.SopRouteLocator;
import com.gitee.sop.gatewaycommon.zuul.route.ZuulForwardChooser;
import com.gitee.sop.gatewaycommon.zuul.route.ZuulRouteCache;
import com.gitee.sop.gatewaycommon.zuul.route.ZuulRouteRepository;
import com.netflix.zuul.context.RequestContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.cloud.netflix.zuul.filters.ProxyRequestHelper;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.cloud.netflix.zuul.filters.pre.PreDecorationFilter;
import org.springframework.context.annotation.Bean;
/**
* @author tanghc
*/
public class BaseZuulConfiguration extends AbstractConfiguration {
@Autowired
protected ZuulProperties zuulProperties;
@Autowired
protected ServerProperties server;
@Bean
public ConfigChannelController configChannelController() {
return new ConfigChannelController();
}
@Bean
public ErrorLogController errorLogController() {
return new ErrorLogController();
}
@Bean
public ZuulIndexController zuulIndexController() {
return new ZuulIndexController();
}
@Bean
public ZuulMonitorController zuulMonitorController() {
return new ZuulMonitorController();
}
@Bean
@ConditionalOnMissingBean
ParamBuilder<RequestContext> paramBuilder() {
return ApiConfig.getInstance().getZuulParamBuilder();
}
/**
* 路由仓库
*/
@Bean
ZuulRouteRepository zuulRouteRepository() {
ZuulRouteRepository zuulRouteRepository = new ZuulRouteRepository();
RouteRepositoryContext.setRouteRepository(zuulRouteRepository);
return zuulRouteRepository;
}
@Bean
PreHttpServletRequestWrapperFilter preHttpServletRequestWrapperFilter() {
return new PreHttpServletRequestWrapperFilter();
}
@Bean
FormBodyWrapperFilterExt formBodyWrapperFilterExt() {
return new FormBodyWrapperFilterExt();
}
@Bean
Servlet30WrapperFilterExt servlet30WrapperFilterExt() {
return new Servlet30WrapperFilterExt();
}
@Bean
SopRouteLocator sopRouteLocator() {
return new SopRouteLocator();
}
/**
* 选取路由
* @param sopRouteLocator
* @param proxyRequestHelper
* @return
*/
@Bean
PreDecorationFilter preDecorationFilter(SopRouteLocator sopRouteLocator, ProxyRequestHelper proxyRequestHelper) {
// 自定义路由
return new PreDecorationFilter(sopRouteLocator,
this.server.getServlet().getContextPath(),
this.zuulProperties,
proxyRequestHelper);
}
/**
* 路由管理
* @param zuulRouteRepository 路由仓库
*/
@Bean
ZuulRouteCache zuulRouteCache(ZuulRouteRepository zuulRouteRepository) {
return new ZuulRouteCache(zuulRouteRepository);
}
@Bean
ValidateService validateService() {
return new ValidateService();
}
@Bean
PreParameterFormatterFilter preParameterFormatterFilter() {
return new PreParameterFormatterFilter();
}
/**
* 开启限流
*/
@Bean
PreLimitFilter preLimitFilter() {
return new PreLimitFilter();
}
/**
* 错误处理扩展
*/
@Bean
ErrorFilter errorFilter() {
return new ErrorFilter();
}
/**
* 结果返回
*/
@Bean
PostResultFilter postResultFilter() {
return new PostResultFilter();
}
/**
* 统一错误处理
*/
@Bean
ZuulErrorController zuulErrorController() {
return ApiContext.getApiConfig().getZuulErrorController();
}
@Bean
ZuulForwardChooser zuulForwardChooser() {
return new ZuulForwardChooser();
}
}

@ -1,23 +0,0 @@
package com.gitee.sop.gatewaycommon.zuul.configuration;
import com.gitee.sop.gatewaycommon.bean.ApiContext;
import com.gitee.sop.gatewaycommon.param.ParamNames;
import com.gitee.sop.gatewaycommon.validate.taobao.TaobaoSigner;
/**
* 具备淘宝开放平台能力配置
* 淘宝开放平台http://open.taobao.com/doc.htm
* @author tanghc
*/
public class TaobaoZuulConfiguration extends BaseZuulConfiguration {
static {
ParamNames.APP_KEY_NAME = "app_key";
ParamNames.SIGN_TYPE_NAME = "sign_method";
ParamNames.VERSION_NAME = "v";
ParamNames.APP_AUTH_TOKEN_NAME = "session";
ApiContext.getApiConfig().setSigner(new TaobaoSigner());
}
}

@ -1,18 +0,0 @@
package com.gitee.sop.gatewaycommon.zuul.configuration;
import com.gitee.sop.gatewaycommon.bean.ApiConfig;
import com.gitee.sop.gatewaycommon.result.CustomDataNameBuilder;
/**
* 支持传统webapp开发没有签名验证
*
* @author tanghc
*/
@Deprecated
public class WebappZuulConfiguration extends BaseZuulConfiguration {
static {
ApiConfig.getInstance().setDataNameBuilder(new CustomDataNameBuilder());
ApiConfig.getInstance().setShowReturnSign(false);
}
}

@ -1,68 +0,0 @@
package com.gitee.sop.gatewaycommon.zuul.controller;
import com.alibaba.fastjson.JSON;
import com.gitee.sop.gatewaycommon.bean.GatewayPushDTO;
import com.gitee.sop.gatewaycommon.bean.NacosConfigs;
import com.gitee.sop.gatewaycommon.bean.SpringContext;
import com.gitee.sop.gatewaycommon.manager.ChannelMsgProcessor;
import com.gitee.sop.gatewaycommon.manager.EnvGrayManager;
import com.gitee.sop.gatewaycommon.manager.IPBlacklistManager;
import com.gitee.sop.gatewaycommon.manager.IsvRoutePermissionManager;
import com.gitee.sop.gatewaycommon.manager.LimitConfigManager;
import com.gitee.sop.gatewaycommon.manager.RouteConfigManager;
import com.gitee.sop.gatewaycommon.secret.IsvManager;
import com.gitee.sop.gatewaycommon.util.RequestUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* @author tanghc
*/
@Slf4j
@RestController
public class ConfigChannelController {
private static Map<String, Class<? extends ChannelMsgProcessor>> processorMap = new HashMap<>(16);
static {
processorMap.put(NacosConfigs.GROUP_CHANNEL + NacosConfigs.DATA_ID_GRAY, EnvGrayManager.class);
processorMap.put(NacosConfigs.GROUP_CHANNEL + NacosConfigs.DATA_ID_IP_BLACKLIST, IPBlacklistManager.class);
processorMap.put(NacosConfigs.GROUP_CHANNEL + NacosConfigs.DATA_ID_ISV, IsvManager.class);
processorMap.put(NacosConfigs.GROUP_CHANNEL + NacosConfigs.DATA_ID_ROUTE_PERMISSION, IsvRoutePermissionManager.class);
processorMap.put(NacosConfigs.GROUP_CHANNEL + NacosConfigs.DATA_ID_LIMIT_CONFIG, LimitConfigManager.class);
processorMap.put(NacosConfigs.GROUP_CHANNEL + NacosConfigs.DATA_ID_ROUTE_CONFIG, RouteConfigManager.class);
}
@Value("${sop.secret}")
private String secret;
@PostMapping("/sop/configChannelMsg")
public String configChannel(HttpServletRequest request) throws IOException {
String requestJson = RequestUtil.getText(request);
String sign = request.getHeader("sign");
try {
RequestUtil.checkResponseBody(requestJson, sign, secret);
} catch (Exception e) {
log.error("configChannelMsg错误", e);
return e.getMessage();
}
GatewayPushDTO gatewayPushDTO = JSON.parseObject(requestJson, GatewayPushDTO.class);
ChannelMsgProcessor channelMsgProcessor = getChannelMsgProcessor(gatewayPushDTO);
channelMsgProcessor.process(gatewayPushDTO.getChannelMsg());
return "ok";
}
private ChannelMsgProcessor getChannelMsgProcessor(GatewayPushDTO gatewayPushDTO) {
String key = gatewayPushDTO.getGroupId() + gatewayPushDTO.getDataId();
Class<? extends ChannelMsgProcessor> aClass = processorMap.get(key);
return SpringContext.getBean(aClass);
}
}

@ -1,23 +0,0 @@
package com.gitee.sop.gatewaycommon.zuul.controller;
import com.gitee.sop.gatewaycommon.bean.BaseErrorLogController;
import com.gitee.sop.gatewaycommon.param.ApiParam;
import com.gitee.sop.gatewaycommon.util.RequestUtil;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
/**
* @author tanghc
*/
@RestController
public class ErrorLogController extends BaseErrorLogController<HttpServletRequest> {
@Override
protected ApiParam getApiParam(HttpServletRequest request) {
Map<String, String> params = RequestUtil.convertRequestParamsToMap(request);
return ApiParam.build(params);
}
}

@ -1,53 +0,0 @@
package com.gitee.sop.gatewaycommon.zuul.controller;
import com.gitee.sop.gatewaycommon.bean.ApiContext;
import com.gitee.sop.gatewaycommon.result.ResultExecutor;
import com.gitee.sop.gatewaycommon.zuul.ZuulContext;
import com.netflix.zuul.context.RequestContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* zuul的异常处理
*
* @author tanghc
*/
@Slf4j
@RestController
public class ZuulErrorController implements ErrorController {
/**
* 错误最终会到这里来
*/
@RequestMapping("/error")
public Object error(HttpServletRequest request, HttpServletResponse response) {
RequestContext ctx = RequestContext.getCurrentContext();
if (ctx.getResponse() == null) {
ctx.setResponse(response);
}
Throwable throwable = ctx.getThrowable();
log.error("zuul网关报错,URL:{}, status:{}, params:{}",
request.getRequestURL().toString()
, response.getStatus()
, ZuulContext.getApiParam()
, throwable);
RequestContext.getCurrentContext().setRequest(request);
return this.buildResult(throwable);
}
protected Object buildResult(Throwable throwable) {
ResultExecutor<RequestContext, String> resultExecutor = ApiContext.getApiConfig().getZuulResultExecutor();
return resultExecutor.buildErrorResult(RequestContext.getCurrentContext(), throwable);
}
@Override
public String getErrorPath() {
return "/error";
}
}

@ -1,84 +0,0 @@
package com.gitee.sop.gatewaycommon.zuul.controller;
import com.gitee.sop.gatewaycommon.param.ApiParam;
import com.gitee.sop.gatewaycommon.util.RequestUtil;
import com.gitee.sop.gatewaycommon.zuul.ValidateService;
import com.gitee.sop.gatewaycommon.zuul.ZuulContext;
import com.netflix.zuul.context.RequestContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* zuul网关入口
*
* @author tanghc
*/
@Slf4j
@Controller
public class ZuulIndexController {
private static final String EMPTY_VERSION = "";
@Autowired
private ValidateService validateService;
@Value("${zuul.servlet-path:/zuul}")
private String path;
@Value("${sop.restful.path:/rest}")
private String restPath;
/**
* 验证回调可自定义实现接口
*/
private ValidateService.ValidateCallback callback = (currentContext -> {
try {
currentContext.getRequest().getRequestDispatcher(path).forward(currentContext.getRequest(), currentContext.getResponse());
} catch (Exception e) {
log.error("请求转发异常", e);
}
});
/**
* 网关入口
*
* @param request request
* @param response response
*/
@RequestMapping("/")
public void index(HttpServletRequest request, HttpServletResponse response) {
RequestContext currentContext = RequestContext.getCurrentContext();
currentContext.setRequest(RequestUtil.wrapRequest(request));
currentContext.setResponse(response);
validateService.validate(currentContext, callback);
}
/**
* restful入口
*
* @param request request
* @param response response
*/
@RequestMapping("/rest/**")
public void rest(HttpServletRequest request, HttpServletResponse response) {
RequestContext currentContext = RequestContext.getCurrentContext();
currentContext.setRequest(RequestUtil.wrapRequest(request));
currentContext.setResponse(response);
String url = request.getRequestURL().toString();
int index = url.indexOf(restPath);
// 取/rest的后面部分
String path = url.substring(index + restPath.length());
ApiParam apiParam = ApiParam.createRestfulApiParam(path);
ZuulContext.setApiParam(apiParam);
validateService.validate(currentContext, callback);
}
}

@ -1,21 +0,0 @@
package com.gitee.sop.gatewaycommon.zuul.controller;
import com.gitee.sop.gatewaycommon.support.BaseMonitorController;
import com.gitee.sop.gatewaycommon.param.ApiParam;
import com.gitee.sop.gatewaycommon.util.RequestUtil;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
/**
* @author tanghc
*/
@RestController
public class ZuulMonitorController extends BaseMonitorController<HttpServletRequest> {
@Override
protected ApiParam getApiParam(HttpServletRequest request) {
Map<String, String> params = RequestUtil.convertRequestParamsToMap(request);
return ApiParam.build(params);
}
}

@ -1,130 +0,0 @@
package com.gitee.sop.gatewaycommon.zuul.filter;
import com.alibaba.fastjson.JSON;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author tanghc
*/
public abstract class BaseZuulFilter extends ZuulFilter {
protected Logger log = LoggerFactory.getLogger(getClass());
public static final int HTTP_SERVLET_REQUEST_WRAPPER_FILTER_ORDER = -2000;
public static final int SERVLET_30_WRAPPER_FILTER_ORDER = HTTP_SERVLET_REQUEST_WRAPPER_FILTER_ORDER + 1;
public static final int FORM_BODY_WRAPPER_FILTER_ORDER = SERVLET_30_WRAPPER_FILTER_ORDER + 1;
/** 签名验证过滤 */
public static final int PRE_VALIDATE_FILTER_ORDER = -1000;
/** 限流过滤 */
public static final int PRE_LIMIT_FILTER_ORDER = -990;
/** 参数格式化过滤器 */
public static final int PRE_PARAMETER_FORMATTER_FILTER_ORDER = -980;
/** 灰度发布过滤器 */
public static final int PRE_ENV_GRAY_FILTER_ORDER = -970;
private Integer filterOrder;
/**
* 获取过滤器类型
* @return 返回FilterType
* @see ZuulFilter#filterType() filterType()
*/
protected abstract FilterType getFilterType();
/**
* 获取过滤器顺序
* @return 返回顺序越小优先执行
* @see ZuulFilter#filterOrder() filterOrder()
*/
protected abstract int getFilterOrder();
/**
* 执行run
* @param requestContext
* @return Some arbitrary artifact may be returned. Current implementation ignores it.
* @throws ZuulException
*/
protected abstract Object doRun(RequestContext requestContext) throws ZuulException;
/**
* 设置过滤器顺序
*
* @param filterOrder 顺序值越小优先执行
* @return 返回自身对象
*/
public BaseZuulFilter order(int filterOrder) {
this.filterOrder = filterOrder;
return this;
}
@Override
public int filterOrder() {
return filterOrder != null ? filterOrder : this.getFilterOrder();
}
@Override
public String filterType() {
return this.getFilterType().getType();
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() throws ZuulException {
return this.doRun(RequestContext.getCurrentContext());
}
/**
* 过滤该请求不往下级服务去转发请求到此结束并填充responseBody
*
* @param requestContext
* @param result
*/
public static void stopRouteAndReturn(RequestContext requestContext, Object result) {
requestContext.setSendZuulResponse(false);
requestContext.setResponseBody(JSON.toJSONString(result));
}
/**
* to classify a filter by type. Standard types in Zuul are "pre" for pre-routing filtering,
* "routeDefinition" for routing to an origin, "post" for post-routing filters, "error" for error handling.
* We also support a "static" type for static responses see StaticResponseFilter.
* Any filterType made be created or added and doRun by calling FilterProcessor.runFilters(type)
*/
public enum FilterType {
/** zuul过滤器pre类型 */
PRE("pre"),
/** zuul过滤器route类型 */
ROUTE("routeDefinition"),
/** zuul过滤器post类型 */
POST("post"),
/** zuul过滤器error类型 */
ERROR("error"),
/** zuul过滤器static类型 */
STATIC("static"),
;
FilterType(String type) {
this.type = type;
}
private String type;
public String getType() {
return type;
}
}
}

@ -1,60 +0,0 @@
package com.gitee.sop.gatewaycommon.zuul.filter;
import com.netflix.zuul.FilterProcessor;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.cloud.netflix.zuul.filters.post.SendErrorFilter;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
/**
* 处理来自post过滤器引起的异常
* @author tanghc
*/
public class ErrorFilter extends SendErrorFilter {
public static final String FAILED_FILTER = "failed.filter";
private int filterOrder = 10;
public ErrorFilter() {
initFilterProcessor();
}
public void initFilterProcessor() {
FilterProcessor instance = FilterProcessor.getInstance();
if (!(instance instanceof MyFilterProcessor)) {
FilterProcessor.setProcessor(new MyFilterProcessor());
}
}
@Override
public int filterOrder() {
return filterOrder;
}
@Override
public boolean shouldFilter() {
// 判断:仅处理来自post过滤器引起的异常
RequestContext ctx = RequestContext.getCurrentContext();
ZuulFilter failedFilter = (ZuulFilter) ctx.get(FAILED_FILTER);
return failedFilter != null && failedFilter.filterType().equals(FilterConstants.POST_TYPE);
}
public static class MyFilterProcessor extends FilterProcessor {
@Override
public Object processZuulFilter(ZuulFilter filter) throws ZuulException {
try {
return super.processZuulFilter(filter);
} catch (ZuulException e) {
RequestContext ctx = RequestContext.getCurrentContext();
ctx.set(FAILED_FILTER, filter);
throw e;
}
}
}
public void setFilterOrder(int filterOrder) {
this.filterOrder = filterOrder;
}
}

@ -1,14 +0,0 @@
package com.gitee.sop.gatewaycommon.zuul.filter;
import org.springframework.cloud.netflix.zuul.filters.pre.FormBodyWrapperFilter;
/**
* @author tanghc
*/
public class FormBodyWrapperFilterExt extends FormBodyWrapperFilter {
@Override
public int filterOrder() {
return BaseZuulFilter.FORM_BODY_WRAPPER_FILTER_ORDER;
}
}

@ -1,75 +0,0 @@
package com.gitee.sop.gatewaycommon.zuul.filter;
import com.gitee.sop.gatewaycommon.bean.ApiConfig;
import com.gitee.sop.gatewaycommon.bean.ApiContext;
import com.gitee.sop.gatewaycommon.bean.SopConstants;
import com.gitee.sop.gatewaycommon.result.ResultExecutor;
import com.gitee.sop.gatewaycommon.zuul.RequestContextUtil;
import com.netflix.util.Pair;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
/**
* 合并微服务结果统一返回格式
*
* @author tanghc
*/
public class PostResultFilter extends BaseZuulFilter {
@Override
protected FilterType getFilterType() {
return FilterType.POST;
}
@Override
protected int getFilterOrder() {
return FilterConstants.SEND_RESPONSE_FILTER_ORDER - 1;
}
@Override
protected Object doRun(RequestContext requestContext) throws ZuulException {
HttpServletResponse response = requestContext.getResponse();
if (response.isCommitted()) {
return null;
}
String contentType = RequestContextUtil.getZuulContentType(requestContext);
// 如果是文件下载直接返回
if (StringUtils.containsIgnoreCase(contentType, MediaType.APPLICATION_OCTET_STREAM_VALUE)) {
return null;
}
ApiConfig apiConfig = ApiContext.getApiConfig();
ResultExecutor<RequestContext, String> resultExecutor = apiConfig.getZuulResultExecutor();
String serviceResult = getServiceResponseBody(requestContext);
String finalResult = resultExecutor.mergeResult(requestContext, serviceResult);
requestContext.setResponseBody(finalResult);
requestContext.getZuulResponseHeaders().add(new Pair<>(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE));
return null;
}
/**
* 获取微服务端返回的结果
*
* @param requestContext RequestContext
* @return 返回结果
*/
private String getServiceResponseBody(RequestContext requestContext) {
String serviceResult;
InputStream responseDataStream = requestContext.getResponseDataStream();
try {
serviceResult = IOUtils.toString(responseDataStream, SopConstants.CHARSET_UTF8);
} catch (Exception e) {
log.error("业务方无数据返回", e);
serviceResult = SopConstants.EMPTY_JSON;
}
return serviceResult;
}
}

@ -1,53 +0,0 @@
package com.gitee.sop.gatewaycommon.zuul.filter;
import com.gitee.sop.gatewaycommon.bean.TargetRoute;
import com.gitee.sop.gatewaycommon.manager.EnvGrayManager;
import com.gitee.sop.gatewaycommon.manager.RouteRepositoryContext;
import com.gitee.sop.gatewaycommon.param.ApiParam;
import com.gitee.sop.gatewaycommon.param.ParamNames;
import com.gitee.sop.gatewaycommon.zuul.ZuulContext;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.beans.factory.annotation.Autowired;
/**
* 灰度发布判断改变版本号
*
* @author tanghc
* @deprecated
* @see com.gitee.sop.gatewaycommon.zuul.route.ZuulForwardChooser
*/
@Deprecated
public class PreEnvGrayFilter extends BaseZuulFilter {
@Autowired
private EnvGrayManager envGrayManager;
@Override
protected FilterType getFilterType() {
return FilterType.PRE;
}
@Override
protected int getFilterOrder() {
return PRE_ENV_GRAY_FILTER_ORDER;
}
@Override
protected Object doRun(RequestContext requestContext) throws ZuulException {
ApiParam apiParam = ZuulContext.getApiParam();
String nameVersion = apiParam.fetchNameVersion();
TargetRoute targetRoute = RouteRepositoryContext.getRouteRepository().get(nameVersion);
if (targetRoute == null) {
return null;
}
String serviceId = targetRoute.getServiceRouteInfo().fetchServiceIdLowerCase();
// 如果服务在灰度阶段,返回一个灰度版本号
String version = envGrayManager.getVersion(serviceId, nameVersion);
if (version != null && envGrayManager.containsKey(serviceId, apiParam.fetchAppKey())) {
requestContext.set(EnvGrayManager.ENV_GRAY, true);
requestContext.addZuulRequestHeader(ParamNames.HEADER_VERSION_NAME, version);
}
return null;
}
}

@ -1,31 +0,0 @@
package com.gitee.sop.gatewaycommon.zuul.filter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import com.netflix.zuul.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletRequest;
/**
* 包装一下Request使得request.getInputStream()方法可以调用多次
* @author tanghc
*/
public class PreHttpServletRequestWrapperFilter extends BaseZuulFilter {
@Override
protected FilterType getFilterType() {
return FilterType.PRE;
}
@Override
protected int getFilterOrder() {
return HTTP_SERVLET_REQUEST_WRAPPER_FILTER_ORDER;
}
@Override
protected Object doRun(RequestContext requestContext) throws ZuulException {
HttpServletRequest request = requestContext.getRequest();
HttpServletRequestWrapper wrapper = new HttpServletRequestWrapper(request);
requestContext.setRequest(wrapper);
return null;
}
}

@ -1,118 +0,0 @@
package com.gitee.sop.gatewaycommon.zuul.filter;
import com.gitee.sop.gatewaycommon.bean.ApiConfig;
import com.gitee.sop.gatewaycommon.bean.ConfigLimitDto;
import com.gitee.sop.gatewaycommon.exception.ApiException;
import com.gitee.sop.gatewaycommon.limit.LimitManager;
import com.gitee.sop.gatewaycommon.limit.LimitType;
import com.gitee.sop.gatewaycommon.manager.LimitConfigManager;
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
import com.gitee.sop.gatewaycommon.message.ErrorMeta;
import com.gitee.sop.gatewaycommon.param.ApiParam;
import com.gitee.sop.gatewaycommon.util.RequestUtil;
import com.gitee.sop.gatewaycommon.zuul.ZuulContext;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.beans.factory.annotation.Autowired;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
/**
* zuul限流过滤器
*
* @author tanghc
*/
public class PreLimitFilter extends BaseZuulFilter {
private static final ErrorMeta LIMIT_ERROR_META = ErrorEnum.ISV_REQUEST_LIMIT.getErrorMeta();
@Autowired
private LimitManager limitManager;
@Autowired
private LimitConfigManager limitConfigManager;
@Override
protected FilterType getFilterType() {
return FilterType.PRE;
}
@Override
protected int getFilterOrder() {
return PRE_LIMIT_FILTER_ORDER;
}
@Override
protected Object doRun(RequestContext requestContext) throws ZuulException {
ApiConfig apiConfig = ApiConfig.getInstance();
// 限流功能未开启,直接返回
if (!apiConfig.isOpenLimit()) {
return null;
}
ApiParam apiParam = ZuulContext.getApiParam();
ConfigLimitDto configLimitDto = this.findConfigLimitDto(apiConfig, apiParam, requestContext.getRequest());
if (configLimitDto == null) {
return null;
}
// 单个限流功能未开启
if (configLimitDto.getLimitStatus() == ConfigLimitDto.LIMIT_STATUS_CLOSE) {
return null;
}
byte limitType = configLimitDto.getLimitType();
// 如果是漏桶策略
if (limitType == LimitType.LEAKY_BUCKET.getType()) {
boolean acquire = limitManager.acquire(configLimitDto);
// 被限流,返回错误信息
if (!acquire) {
throw new ApiException(LIMIT_ERROR_META);
}
} else if (limitType == LimitType.TOKEN_BUCKET.getType()) {
limitManager.acquireToken(configLimitDto);
}
return null;
}
protected ConfigLimitDto findConfigLimitDto(ApiConfig apiConfig, ApiParam apiParam, HttpServletRequest request) {
String routeId = apiParam.fetchNameVersion();
String appKey = apiParam.fetchAppKey();
String ip = RequestUtil.getIP(request);
// 最多7种情况
String[] limitKeys = new String[]{
// 根据路由ID限流
routeId,
// 根据appKey限流
appKey,
// 根据路由ID + appKey限流
routeId + appKey,
// 根据ip限流
ip,
// 根据ip+路由id限流
ip + routeId,
// 根据ip+appKey限流
ip + appKey,
// 根据ip+路由id+appKey限流
ip + routeId + appKey,
};
List<ConfigLimitDto> limitConfigList = new ArrayList<>();
for (String limitKey : limitKeys) {
ConfigLimitDto configLimitDto = limitConfigManager.get(limitKey);
if (configLimitDto == null) {
continue;
}
if (configLimitDto.getLimitStatus().intValue() == ConfigLimitDto.LIMIT_STATUS_OPEN) {
limitConfigList.add(configLimitDto);
}
}
if (limitConfigList.isEmpty()) {
return null;
}
limitConfigList.sort(Comparator.comparing(ConfigLimitDto::getOrderIndex));
return limitConfigList.get(0);
}
}

@ -1,42 +0,0 @@
package com.gitee.sop.gatewaycommon.zuul.filter;
import com.gitee.sop.gatewaycommon.param.ApiParam;
import com.gitee.sop.gatewaycommon.param.ParamNames;
import com.gitee.sop.gatewaycommon.param.ParameterFormatter;
import com.gitee.sop.gatewaycommon.zuul.ZuulContext;
import com.gitee.sop.gatewaycommon.zuul.param.ZuulParameterUtil;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.beans.factory.annotation.Autowired;
/**
* 参数格式化过滤器动态修改参数此过滤器放在前面校验后面
*
* @author tanghc
*/
public class PreParameterFormatterFilter extends BaseZuulFilter {
@Autowired(required = false)
private ParameterFormatter parameterFormatter;
@Override
protected FilterType getFilterType() {
return FilterType.PRE;
}
@Override
protected int getFilterOrder() {
return PRE_PARAMETER_FORMATTER_FILTER_ORDER;
}
@Override
protected Object doRun(RequestContext requestContext) throws ZuulException {
ApiParam apiParam = ZuulContext.getApiParam();
// 校验成功后进行参数转换
if (parameterFormatter != null) {
ZuulParameterUtil.format(apiParam, parameterFormatter::format);
requestContext.addZuulRequestHeader(ParamNames.HEADER_VERSION_NAME, apiParam.fetchVersion());
}
return null;
}
}

@ -1,30 +0,0 @@
package com.gitee.sop.gatewaycommon.zuul.filter;
import com.netflix.zuul.context.RequestContext;
/**
* 校验工作转移到了 com.gitee.sop.gateway.controller.RedirectController
* <p>
* 将校验工作提前如果在zuul过滤器中校验抛出异常将会打印非常多的日志并且无法实现自定义返回结果
* @deprecated see {@link com.gitee.sop.gatewaycommon.zuul.ValidateService}
* @author tanghc
*/
@Deprecated
public class PreValidateFilter extends BaseZuulFilter {
@Override
protected FilterType getFilterType() {
return FilterType.PRE;
}
@Override
protected int getFilterOrder() {
return PRE_VALIDATE_FILTER_ORDER;
}
@Override
protected Object doRun(RequestContext requestContext) {
return null;
}
}

@ -1,14 +0,0 @@
package com.gitee.sop.gatewaycommon.zuul.filter;
import org.springframework.cloud.netflix.zuul.filters.pre.Servlet30WrapperFilter;
/**
* @author tanghc
*/
public class Servlet30WrapperFilterExt extends Servlet30WrapperFilter {
@Override
public int filterOrder() {
return BaseZuulFilter.SERVLET_30_WRAPPER_FILTER_ORDER;
}
}

@ -1,55 +0,0 @@
package com.gitee.sop.gatewaycommon.zuul.loadbalancer;
import com.gitee.sop.gatewaycommon.loadbalancer.ServerChooserContext;
import com.gitee.sop.gatewaycommon.param.ApiParam;
import com.gitee.sop.gatewaycommon.zuul.ZuulContext;
import com.google.common.base.Optional;
import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.ZoneAvoidanceRule;
import com.netflix.zuul.context.RequestContext;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
/**
* 预发布灰度环境选择参考自https://segmentfault.com/a/1190000017412946
*
* @author tanghc
*/
@Slf4j
public class EnvironmentServerChooser extends ZoneAvoidanceRule implements ServerChooserContext<HttpServletRequest> {
private ZuulLoadBalanceServerChooser loadBalanceServerChooser = new ZuulLoadBalanceServerChooser();
@Override
public String getHost(HttpServletRequest request) {
return request.getServerName();
}
@Override
public ApiParam getApiParam(HttpServletRequest request) {
return ZuulContext.getApiParam();
}
@Override
public Server choose(Object key) {
return loadBalanceServerChooser.choose(
String.valueOf(key)
, RequestContext.getCurrentContext().getRequest()
, getLoadBalancer()
, () -> super.choose(key)
, (servers) -> this.doChoose(servers, key)
);
}
protected Server doChoose(List<Server> servers, Object key) {
Optional<Server> server = getPredicate().chooseRoundRobinAfterFiltering(servers, key);
if (server.isPresent()) {
return server.get();
} else {
return null;
}
}
}

@ -1,24 +0,0 @@
package com.gitee.sop.gatewaycommon.zuul.loadbalancer;
import com.gitee.sop.gatewaycommon.loadbalancer.LoadBalanceServerChooser;
import com.gitee.sop.gatewaycommon.param.ApiParam;
import com.gitee.sop.gatewaycommon.zuul.ZuulContext;
import com.netflix.loadbalancer.Server;
import javax.servlet.http.HttpServletRequest;
/**
* @author tanghc
*/
public class ZuulLoadBalanceServerChooser extends LoadBalanceServerChooser<HttpServletRequest, Server> {
@Override
public String getHost(HttpServletRequest request) {
return request.getServerName();
}
@Override
public ApiParam getApiParam(HttpServletRequest request) {
return ZuulContext.getApiParam();
}
}

@ -1,67 +0,0 @@
package com.gitee.sop.gatewaycommon.zuul.param;
import com.gitee.sop.gatewaycommon.param.ApiParam;
import com.gitee.sop.gatewaycommon.param.BaseParamBuilder;
import com.gitee.sop.gatewaycommon.util.RequestUtil;
import com.netflix.zuul.context.RequestContext;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.MediaType;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
/**
* 参数解析默认实现
*
* @author tanghc
*/
@Slf4j
public class ZuulParamBuilder extends BaseParamBuilder<RequestContext> {
private static final String GET = "get";
@Override
public ApiParam buildRequestParams(RequestContext ctx) {
HttpServletRequest request = ctx.getRequest();
Map<String, ?> params;
ApiParam apiParam = new ApiParam();
if (GET.equalsIgnoreCase(request.getMethod())) {
params = RequestUtil.convertRequestParamsToMap(request);
} else {
String contentType = request.getContentType();
if (contentType == null) {
contentType = "";
}
contentType = contentType.toLowerCase();
// json或者纯文本形式
if (StringUtils.containsAny(contentType, MediaType.APPLICATION_JSON_VALUE, MediaType.TEXT_PLAIN_VALUE)) {
params = RequestUtil.convertJsonRequestToMap(request);
} else if (ServletFileUpload.isMultipartContent(request)) {
RequestUtil.UploadInfo uploadInfo = RequestUtil.getUploadInfo(request);
params = uploadInfo.getUploadParams();
apiParam.setUploadContext(uploadInfo.getUploadContext());
} else {
params = RequestUtil.convertRequestParamsToMap(request);
}
}
apiParam.putAll(params);
return apiParam;
}
@Override
public String getIP(RequestContext ctx) {
return RequestUtil.getIP(ctx.getRequest());
}
@Override
public void setVersionInHeader(RequestContext ctx, String headerName, String version) {
ctx.addZuulRequestHeader(headerName, version);
}
@Override
protected void processApiParam(ApiParam apiParam, RequestContext ctx) {
}
}

@ -1,141 +0,0 @@
package com.gitee.sop.gatewaycommon.zuul.param;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.gitee.sop.gatewaycommon.manager.EnvironmentKeys;
import com.gitee.sop.gatewaycommon.param.FormHttpOutputMessage;
import com.gitee.sop.gatewaycommon.util.RequestUtil;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.http.HttpServletRequestWrapper;
import com.netflix.zuul.http.ServletInputStreamWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.netflix.zuul.util.RequestContentDataExtractor;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.util.MultiValueMap;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.stream.Collectors;
/**
* zuul参数工具
* @author tanghc
*/
@Slf4j
public class ZuulParameterUtil {
private static FormHttpMessageConverter formHttpMessageConverter = new FormHttpMessageConverter();
/**
* 格式化参数
* @param apiParam 请求的参数
* @param consumer 修改参数
* @param <T> 参数类型
*/
public static <T extends Map<String, Object>> void format(T apiParam, Consumer<T> consumer) {
String restfulEnableValue = EnvironmentKeys.SOP_RESTFUL_ENABLE.getValue();
// restful请求不支持动态修改参数
if ("true".equals(restfulEnableValue)) {
return;
}
RequestContext requestContext = RequestContext.getCurrentContext();
consumer.accept(apiParam);
HttpServletRequest request = requestContext.getRequest();
String contentType = request.getContentType();
if (StringUtils.containsIgnoreCase(contentType, MediaType.APPLICATION_JSON_VALUE)) {
String json = (apiParam instanceof JSONObject) ?
((JSONObject) apiParam).toJSONString()
: JSON.toJSONString(apiParam);
byte[] bytes = json.getBytes(StandardCharsets.UTF_8);
requestContext.setRequest(new BodyDataHttpServletRequestWrapper(request, bytes));
} else if(StringUtils.containsIgnoreCase(contentType, MediaType.APPLICATION_FORM_URLENCODED_VALUE)) {
String paramsStr = RequestUtil.convertMapToQueryString(apiParam);
byte[] data = paramsStr.getBytes(StandardCharsets.UTF_8);
requestContext.setRequest(new BodyDataHttpServletRequestWrapper(request, data));
} else if(RequestUtil.isMultipart(request)) {
FormHttpOutputMessage outputMessage = new FormHttpOutputMessage();
try {
// 转成MultipartRequest
if (!(request instanceof MultipartHttpServletRequest)) {
CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver(request.getServletContext());
request = commonsMultipartResolver.resolveMultipart(request);
}
// 重写新的值
MultiValueMap<String, Object> builder = RequestContentDataExtractor.extract(request);
for (Map.Entry<String, Object> entry : apiParam.entrySet()) {
Object value = entry.getValue();
if (value instanceof List) {
builder.put(entry.getKey(), (List)value);
} else {
builder.put(entry.getKey(), Collections.singletonList(String.valueOf(value)));
}
}
MediaType mediaType = MediaType.valueOf(request.getContentType());
// 将字段以及上传文件重写写入到流中
formHttpMessageConverter.write(builder, mediaType, outputMessage);
// 获取新的上传文件流
byte[] data = outputMessage.getInput();
requestContext.setRequest(new BodyDataHttpServletRequestWrapper(request, data));
// 必须要重新指定content-type,因为此时的boundary已经发生改变
requestContext.getZuulRequestHeaders().put("content-type", outputMessage.getHeaders().getContentType().toString());
} catch (Exception e) {
log.error("修改上传文件请求参数失败, apiParam:{}", apiParam, e);
}
} else if(HttpMethod.GET.name().equalsIgnoreCase(request.getMethod())) {
Map<String, List<String>> newParams = new HashMap<>(apiParam.size() * 2);
for (Map.Entry<String, Object> entry : apiParam.entrySet()) {
Object value = entry.getValue();
if (value instanceof List) {
List<String> valueList = ((List<?>) value).stream().map(String::valueOf).collect(Collectors.toList());
newParams.put(entry.getKey(), valueList);
} else {
newParams.put(entry.getKey(), Collections.singletonList(String.valueOf(value)));
}
}
requestContext.setRequestQueryParams(newParams);
}
}
public static class BodyDataHttpServletRequestWrapper extends HttpServletRequestWrapper {
private byte[] data;
public BodyDataHttpServletRequestWrapper(HttpServletRequest request, byte[] data) {
super(request);
this.data = data;
}
@Override
public ServletInputStream getInputStream() throws IOException {
return new ServletInputStreamWrapper(data);
}
@Override
public byte[] getContentData() {
return data;
}
@Override
public int getContentLength() {
return data.length;
}
@Override
public long getContentLengthLong() {
return data.length;
}
}
}

@ -1,115 +0,0 @@
package com.gitee.sop.gatewaycommon.zuul.result;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.gitee.sop.gatewaycommon.interceptor.RouteInterceptorContext;
import com.gitee.sop.gatewaycommon.bean.SopConstants;
import com.gitee.sop.gatewaycommon.exception.ApiException;
import com.gitee.sop.gatewaycommon.message.Error;
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
import com.gitee.sop.gatewaycommon.param.ApiParam;
import com.gitee.sop.gatewaycommon.result.BaseExecutorAdapter;
import com.gitee.sop.gatewaycommon.result.ResultExecutorForZuul;
import com.gitee.sop.gatewaycommon.zuul.ZuulContext;
import com.netflix.util.Pair;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import org.springframework.web.util.UriUtils;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Locale;
import java.util.function.Consumer;
/**
* @author tanghc
*/
@Slf4j
public class ZuulResultExecutor extends BaseExecutorAdapter<RequestContext, String> implements ResultExecutorForZuul {
@Override
public int getResponseStatus(RequestContext requestContext) {
List<Pair<String, String>> bizHeaders = requestContext.getZuulResponseHeaders();
return bizHeaders.stream()
.filter(header -> SopConstants.X_SERVICE_ERROR_CODE.equals(header.first()))
.map(header -> Integer.valueOf(header.second()))
.findFirst()
.orElse(requestContext.getResponseStatusCode());
}
@Override
public String getResponseErrorMessage(RequestContext requestContext) {
String errorMsg = getHeader(requestContext, SopConstants.X_SERVICE_ERROR_MESSAGE, (index)->{
if (index > -1) {
requestContext.getZuulResponseHeaders().remove(index);
}
});
if (StringUtils.hasText(errorMsg)) {
errorMsg = UriUtils.decode(errorMsg, StandardCharsets.UTF_8);
}
return errorMsg;
}
@Override
public ApiParam getApiParam(RequestContext requestContext) {
return ZuulContext.getApiParam();
}
@Override
protected Locale getLocale(RequestContext requestContext) {
return requestContext.getRequest().getLocale();
}
@Override
protected RouteInterceptorContext getRouteInterceptorContext(RequestContext requestContext) {
return (RouteInterceptorContext) requestContext.get(SopConstants.CACHE_ROUTE_INTERCEPTOR_CONTEXT);
}
@Override
public String buildErrorResult(RequestContext requestContext, Throwable throwable) {
Locale locale = getLocale(requestContext);
Error error = getError(locale, throwable);
return isMergeResult(requestContext) ? this.merge(requestContext, (JSONObject) JSON.toJSON(error))
: JSON.toJSONString(error);
}
public static Error getError(Locale locale, Throwable throwable) {
Error error = null;
if (throwable instanceof ZuulException) {
ZuulException ex = (ZuulException) throwable;
Throwable cause = ex.getCause();
if (cause instanceof ApiException) {
ApiException apiException = (ApiException) cause;
error = apiException.getError(locale);
}
} else if (throwable instanceof ApiException) {
ApiException apiException = (ApiException) throwable;
error = apiException.getError(locale);
}
if (error == null) {
error = ErrorEnum.ISP_UNKNOWN_ERROR.getErrorMeta().getError(locale);
}
return error;
}
private String getHeader(RequestContext requestContext, String name, Consumer<Integer> after) {
List<Pair<String, String>> bizHeaders = requestContext.getZuulResponseHeaders();
int index = -1;
String value = null;
for (int i = 0; i < bizHeaders.size(); i++) {
Pair<String, String> header = bizHeaders.get(i);
if (name.equals(header.first())) {
value = header.second();
index = i;
break;
}
}
if (after != null) {
after.accept(index);
}
return value;
}
}

@ -1,63 +0,0 @@
package com.gitee.sop.gatewaycommon.zuul.route;
import com.gitee.sop.gatewaycommon.bean.AbstractTargetRoute;
import com.gitee.sop.gatewaycommon.param.ApiParam;
import com.gitee.sop.gatewaycommon.route.ForwardInfo;
import com.gitee.sop.gatewaycommon.param.ParamNames;
import com.gitee.sop.gatewaycommon.zuul.ZuulContext;
import com.netflix.zuul.context.RequestContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.netflix.zuul.filters.Route;
import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
import org.springframework.core.Ordered;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
/**
* 路由定位
*
* @author tanghc
*/
public class SopRouteLocator implements RouteLocator, Ordered {
@Autowired
private ZuulRouteRepository zuulRouteRepository;
@Autowired
private ZuulForwardChooser zuulForwardChooser;
@Override
public Collection<String> getIgnoredPaths() {
return Collections.emptyList();
}
@Override
public List<Route> getRoutes() {
return zuulRouteRepository.getAll()
.parallelStream()
.map(AbstractTargetRoute::getTargetRouteDefinition)
.collect(Collectors.toList());
}
/**
* 这里决定使用哪个路由
*
* @param path 当前请求路径
* @return 返回跳转的路由
*/
@Override
public Route getMatchingRoute(String path) {
ForwardInfo forwardInfo = zuulForwardChooser.getForwardInfo(RequestContext.getCurrentContext());
String version = forwardInfo.getVersion();
RequestContext.getCurrentContext().addZuulRequestHeader(ParamNames.HEADER_VERSION_NAME, version);
return (Route)forwardInfo.getTargetRoute().getTargetRouteDefinition();
}
@Override
public int getOrder() {
return 0;
}
}

@ -1,18 +0,0 @@
package com.gitee.sop.gatewaycommon.zuul.route;
import com.gitee.sop.gatewaycommon.param.ApiParam;
import com.gitee.sop.gatewaycommon.route.BaseForwardChooser;
import com.gitee.sop.gatewaycommon.zuul.ZuulContext;
import com.netflix.zuul.context.RequestContext;
/**
* @author tanghc
*/
public class ZuulForwardChooser extends BaseForwardChooser<RequestContext> {
@Override
public ApiParam getApiParam(RequestContext requestContext) {
return ZuulContext.getApiParam();
}
}

@ -1,34 +0,0 @@
package com.gitee.sop.gatewaycommon.zuul.route;
import com.gitee.sop.gatewaycommon.bean.RouteDefinition;
import com.gitee.sop.gatewaycommon.bean.ServiceRouteInfo;
import com.gitee.sop.gatewaycommon.manager.BaseRouteCache;
import com.gitee.sop.gatewaycommon.manager.RouteRepository;
import com.gitee.sop.gatewaycommon.util.RouteUtil;
import org.springframework.cloud.netflix.zuul.filters.Route;
/**
* @author tanghc
*/
public class ZuulRouteCache extends BaseRouteCache<ZuulTargetRoute> {
/** 路由重试 */
private static final boolean RETRYABLE = true;
public ZuulRouteCache(RouteRepository<ZuulTargetRoute> routeRepository) {
super(routeRepository);
}
@Override
protected ZuulTargetRoute buildTargetRoute(ServiceRouteInfo serviceRouteInfo, RouteDefinition gatewayRouteDefinition) {
Route route = new Route(
gatewayRouteDefinition.getId()
, gatewayRouteDefinition.getPath()
, RouteUtil.getZuulLocation(gatewayRouteDefinition.getUri())
, ""
, RETRYABLE
, null
);
return new ZuulTargetRoute(serviceRouteInfo, gatewayRouteDefinition, route);
}
}

@ -1,98 +0,0 @@
package com.gitee.sop.gatewaycommon.zuul.route;
import com.gitee.sop.gatewaycommon.manager.RouteRepository;
import org.springframework.cloud.netflix.zuul.filters.Route;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
/**
* 本地存放路由内容的地方
*
* @author tanghc
*/
public class ZuulRouteRepository implements RouteRepository<ZuulTargetRoute> {
private final PathMatcher pathMatcher = new AntPathMatcher();
/**
* keynameVersion
*/
private static final Map<String, ZuulTargetRoute> nameVersionTargetRouteMap = new ConcurrentHashMap<>(128);
@Override
public ZuulTargetRoute get(String id) {
if (id == null) {
return null;
}
ZuulTargetRoute zuulTargetRoute = nameVersionTargetRouteMap.get(id);
if (zuulTargetRoute != null) {
return zuulTargetRoute;
}
for (Map.Entry<String, ZuulTargetRoute> entry : nameVersionTargetRouteMap.entrySet()) {
String pattern = entry.getKey();
if (this.pathMatcher.match(pattern, id)) {
return clone(id, entry.getValue());
}
}
return null;
}
private ZuulTargetRoute clone(String path, ZuulTargetRoute zuulTargetRoute) {
Route targetRouteDefinition = zuulTargetRoute.getTargetRouteDefinition();
String prefix = "/" + zuulTargetRoute.getServiceRouteInfo().getServiceId();
if (path.startsWith(prefix)) {
path = path.substring(prefix.length());
}
Route route = new Route(
targetRouteDefinition.getId()
,path
,targetRouteDefinition.getLocation()
,targetRouteDefinition.getPrefix()
,targetRouteDefinition.getRetryable()
, null
);
return new ZuulTargetRoute(zuulTargetRoute.getServiceRouteInfo()
, zuulTargetRoute.getRouteDefinition()
, route);
}
@Override
public Collection<ZuulTargetRoute> getAll() {
return nameVersionTargetRouteMap.values();
}
@Override
public String add(ZuulTargetRoute targetRoute) {
nameVersionTargetRouteMap.put(targetRoute.getRouteDefinition().getId(), targetRoute);
return targetRoute.getRouteDefinition().getId();
}
@Override
public void update(ZuulTargetRoute targetRoute) {
nameVersionTargetRouteMap.put(targetRoute.getRouteDefinition().getId(), targetRoute);
}
@Override
public void deleteAll(String serviceId) {
Collection<ZuulTargetRoute> values = nameVersionTargetRouteMap.values();
List<String> idList = values.stream()
.filter(zuulTargetRoute -> zuulTargetRoute.getServiceRouteInfo().getServiceId().equalsIgnoreCase(serviceId))
.map(zuulTargetRoute -> zuulTargetRoute.getRouteDefinition().getId())
.collect(Collectors.toList());
for (String id : idList) {
this.delete(id);
}
}
@Override
public void delete(String id) {
nameVersionTargetRouteMap.remove(id);
}
}

@ -1,18 +0,0 @@
package com.gitee.sop.gatewaycommon.zuul.route;
import com.gitee.sop.gatewaycommon.bean.AbstractTargetRoute;
import com.gitee.sop.gatewaycommon.bean.RouteDefinition;
import com.gitee.sop.gatewaycommon.bean.ServiceRouteInfo;
import lombok.Getter;
import org.springframework.cloud.netflix.zuul.filters.Route;
/**
* @author tanghc
*/
@Getter
public class ZuulTargetRoute extends AbstractTargetRoute<Route> {
public ZuulTargetRoute(ServiceRouteInfo serviceRouteInfo, RouteDefinition routeDefinition, Route targetRoute) {
super(serviceRouteInfo, routeDefinition, targetRoute);
}
}

@ -0,0 +1,39 @@
# 固定不变,不能改
spring.application.name=sop-gateway
# 不用改,如果要改,请全局替换修改
sop.secret=MZZOUSTua6LzApIWXCwEgbBmxSzpzC
# 网关入口
sop.gateway-index-path=/
# nacos cloud配置
spring.cloud.nacos.discovery.server-addr=${register.url}
# 数据库配置
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://${mysql.host}/sop?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai
spring.datasource.username=${mysql.username}
spring.datasource.password=${mysql.password}
# https://blog.csdn.net/qq_36872046/article/details/81058045
# 路由转发超时时间,毫秒,默认值1000,详见:RibbonClientConfiguration.DEFAULT_READ_TIMEOUT。
# 如果微服务端 处理时间过长,会导致ribbon read超时,解决办法将这个值调大一点
ribbon.ReadTimeout=2000
# 设置为true(默认false),则所有请求都重试,默认只支持get请求重试
# 请谨慎设置,因为post请求大多都是写入请求,如果要支持重试,确保服务的幂等性
ribbon.OkToRetryOnAllOperations=false
spring.cloud.gateway.discovery.locator.lower-case-service-id=true
spring.cloud.gateway.discovery.locator.enabled=true
# 不用改
mybatis.fill.com.gitee.fastmybatis.core.support.DateFillInsert=gmt_create
mybatis.fill.com.gitee.fastmybatis.core.support.DateFillUpdate=gmt_modified
# 文件上传配置
spring.servlet.multipart.enabled=true
# 这里设置大一点没关系,真实大小由upload.max-file-size控制
spring.servlet.multipart.max-file-size=50MB
# 允许上传文件大小,不能超过这个值,单位:B,KB,MB
upload.max-file-size=2MB

@ -1,92 +0,0 @@
package com.gitee.sop.servercommon.annotation;
import org.springframework.core.annotation.AliasFor;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 接口申明注解使用方式同RequestMapping一样多了一个版本号属性
* 用了此注解具备开放平台接口提供能力
* @author tanghc
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RequestMapping
public @interface ApiMapping {
// ------------ 自定义属性 ------------
/**
* 版本号默认版本号是""<br>
* 改默认版本号<code>ServiceConfig.getInstance().setDefaultVersion("1.0");</code>
*/
String version() default "";
/**
* 忽略验证业务参数除外
*/
boolean ignoreValidate() default false;
/**
* 告诉网关是否对结果进行合并默认合并设置为false客户端将直接收到微服务端的结果
*/
boolean mergeResult() default true;
/**
* 指定接口是否需要授权才能访问可在admin中进行修改
*/
boolean permission() default false;
/**
* 是否需要appAuthToken设置为true网关端会校验token是否存在
*/
boolean needToken() default false;
// ------------ 自定义属性 end ------------
// ============ 以下是springmvc自带的属性 ============
/**
* 接口名
* Alias for {@link RequestMapping#value}.
*/
@AliasFor(annotation = RequestMapping.class)
String[] value() default {};
@AliasFor(annotation = RequestMapping.class)
RequestMethod[] method() default {};
/**
* Alias for {@link RequestMapping#params}.
*/
@AliasFor(annotation = RequestMapping.class)
String[] params() default {};
/**
* Alias for {@link RequestMapping#headers}.
*/
@AliasFor(annotation = RequestMapping.class)
String[] headers() default {};
/**
* Alias for {@link RequestMapping#consumes}.
*/
@AliasFor(annotation = RequestMapping.class)
String[] consumes() default {};
/**
* Alias for {@link RequestMapping#produces}.
*/
@AliasFor(annotation = RequestMapping.class)
String[] produces() default {};
}

@ -7,17 +7,21 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 设置接口能力
* 如果想把已经存在的接口开放出去可用此注解<br>
* 作用于Controller类上或方法上如果作用在类上则类中的所有方法将具备开放平台接口提供能力
* @author tanghc
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ApiAbility {
public @interface Open {
/**
* 接口名member.user.get
*/
String value();
/**
* 版本号1.0
* 版本号默认版本号是""<br>
* 改默认版本号<code>ServiceConfig.getInstance().setDefaultVersion("1.0");</code>
*/
String version() default "";

@ -1,108 +0,0 @@
package com.gitee.sop.servercommon.bean;
import java.util.Date;
/**
* 获取开放平台请求参数
* 可当做接口方法参数泛型参数填业务参数类:
* <pre>
* <code>@ApiMapping(value = "alipay.goods.get")</code>
* public Goods getGoods(OpenRequest&lt;GoodsParam&gt; openRequest){...}</code> <br/>
* </pre>
* 可通过<code>OpenRequest openRequest = ServiceContext.getCurrentContext().getOpenRequest();</code>方式进行获取
* OpenContext
* OpenParam
*
* @author tanghc
*/
public interface OpenContext<T> extends OpenBeanFactory {
/**
* 返回appid
*
* @return 返回appid
*/
String getAppId();
/**
* 返回业务参数json字符串
*
* @return 返回字符串业务参数
*/
String getBizContent();
/**
* 返回业务对象
*
* @return 业务对象
*/
T getBizObject();
/**
* 返回字符编码
*
* @return 如UTF-8
*/
String getCharset();
/**
* 返回接口名
*
* @return alipay.goods.get
*/
String getMethod();
/**
* 返回版本号
*
* @return 1.0
*/
String getVersion();
/**
* 返回参数格式化
*
* @return json
*/
String getFormat();
/**
* 返回签名类型
*
* @return RSA2
*/
String getSignType();
/**
* 返回时间戳
*
* @return
*/
Date getTimestamp();
/**
* 返回token即access_token.
*
* @deprecated 废弃使用getAppAuthToken()
* @return 返回token
*/
@Deprecated
String appAuthToken();
/**
* 返回token即access_token
*
* @return 返回token
*/
default String getAppAuthToken() {
return appAuthToken();
}
/**
* 返回回调地址
*
* @return 返回回调地址
*/
String getNotifyUrl();
}

@ -1,124 +0,0 @@
package com.gitee.sop.servercommon.bean;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.time.DateUtils;
import java.text.ParseException;
import java.util.Date;
import static com.gitee.sop.servercommon.bean.ParamNames.API_NAME;
import static com.gitee.sop.servercommon.bean.ParamNames.APP_AUTH_TOKEN_NAME;
import static com.gitee.sop.servercommon.bean.ParamNames.APP_KEY_NAME;
import static com.gitee.sop.servercommon.bean.ParamNames.BIZ_CONTENT_NAME;
import static com.gitee.sop.servercommon.bean.ParamNames.CHARSET_NAME;
import static com.gitee.sop.servercommon.bean.ParamNames.FORMAT_NAME;
import static com.gitee.sop.servercommon.bean.ParamNames.NOTIFY_URL_NAME;
import static com.gitee.sop.servercommon.bean.ParamNames.SIGN_TYPE_NAME;
import static com.gitee.sop.servercommon.bean.ParamNames.TIMESTAMP_NAME;
import static com.gitee.sop.servercommon.bean.ParamNames.TIMESTAMP_PATTERN;
import static com.gitee.sop.servercommon.bean.ParamNames.VERSION_NAME;
/**
* @author tanghc
*/
public class OpenContextImpl<T> implements OpenContext<T> {
private JSONObject rootJsonObject;
private T bizObject;
public OpenContextImpl(JSONObject rootJsonObject) {
this(rootJsonObject, null);
}
public OpenContextImpl(JSONObject rootJsonObject, Class<?> bizClass) {
if (rootJsonObject == null) {
throw new IllegalArgumentException("rootJsonObject can not be null");
}
this.rootJsonObject = rootJsonObject;
if (bizClass != null) {
String bizContent = getBizContent();
if (bizContent != null) {
bizObject = (T) JSON.parseObject(bizContent, bizClass);
} else {
bizObject = (T) rootJsonObject.toJavaObject(bizClass);
}
}
}
@Override
public String getAppId() {
return rootJsonObject.getString(APP_KEY_NAME);
}
@Override
public T getBizObject() {
return bizObject;
}
@Override
public String getBizContent() {
return rootJsonObject.getString(BIZ_CONTENT_NAME);
}
@Override
public String getCharset() {
return rootJsonObject.getString(CHARSET_NAME);
}
@Override
public String getMethod() {
return rootJsonObject.getString(API_NAME);
}
@Override
public String getVersion() {
return rootJsonObject.getString(VERSION_NAME);
}
@Override
public String getFormat() {
return rootJsonObject.getString(FORMAT_NAME);
}
@Override
public String getSignType() {
return rootJsonObject.getString(SIGN_TYPE_NAME);
}
@Override
public Date getTimestamp() {
String timestampStr = rootJsonObject.getString(TIMESTAMP_NAME);
try {
return DateUtils.parseDate(timestampStr, TIMESTAMP_PATTERN);
} catch (ParseException e) {
return null;
}
}
@Override
public String appAuthToken() {
return rootJsonObject.getString(APP_AUTH_TOKEN_NAME);
}
@Override
public String getNotifyUrl() {
return rootJsonObject.getString(NOTIFY_URL_NAME);
}
@Override
public <E> E getBizObject(Class<E> clazz) {
if (bizObject != null && bizObject.getClass() == clazz) {
return (E) bizObject;
}
String bizContent = getBizContent();
if (bizContent == null) {
return null;
}
return JSON.parseObject(bizContent, clazz);
}
@Override
public String toString() {
return rootJsonObject.toString();
}
}

@ -34,10 +34,6 @@ public class ServiceApiInfo {
private int permission;
/** 是否需要token */
private int needToken;
/** 是否是兼容模式,即使用了@ApiAbility注解 */
private int compatibleMode;
/** 是否是原始Mapping */
private boolean originalMapping;
public ApiMeta() {
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save