mirror of
https://gitee.com/durcframework/SOP.git
synced 2025-08-11 21:57:56 +08:00
4.0
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -15,6 +15,7 @@
|
|||||||
*.iws
|
*.iws
|
||||||
*.iml
|
*.iml
|
||||||
*.ipr
|
*.ipr
|
||||||
|
*.png
|
||||||
|
|
||||||
### NetBeans ###
|
### NetBeans ###
|
||||||
/nbproject/private/
|
/nbproject/private/
|
||||||
|
13
changelog.md
13
changelog.md
@@ -1,5 +1,18 @@
|
|||||||
# changelog
|
# changelog
|
||||||
|
|
||||||
|
## 4.0.0(不兼容3.x)
|
||||||
|
|
||||||
|
- 新增@Open注解,代替ApiMapping和ApiAbility
|
||||||
|
- service接入减少代码入侵
|
||||||
|
- 修复admin密码存储规则
|
||||||
|
|
||||||
|
### 不兼容部分
|
||||||
|
|
||||||
|
- 移除ApiMapping和ApiAbility,改为Open注解
|
||||||
|
- 移除OpenContext
|
||||||
|
- 移除zuul
|
||||||
|
- 移除对easyopen支持
|
||||||
|
|
||||||
## 3.2.1
|
## 3.2.1
|
||||||
|
|
||||||
- 强化RouteInterceptorContext,可获取微服务信息
|
- 强化RouteInterceptorContext,可获取微服务信息
|
||||||
|
@@ -1,39 +1,34 @@
|
|||||||
* [首页](/?t=1595832340981)
|
* [首页](/?t=1595931646391)
|
||||||
* 开发文档
|
* 开发文档
|
||||||
* [快速体验](files/10010_快速体验.md?t=1595832340983)
|
* [快速体验](files/10010_快速体验.md?t=1595931646394)
|
||||||
* [项目接入到SOP](files/10011_项目接入到SOP.md?t=1595832341000)
|
* [项目接入到SOP](files/10011_项目接入到SOP.md?t=1595931646412)
|
||||||
* [新增接口](files/10020_新增接口.md?t=1595832341000)
|
* [新增接口](files/10020_新增接口.md?t=1595931646412)
|
||||||
* [开发流程](files/10021_开发流程.md?t=1595832341001)
|
* [开发流程](files/10021_开发流程.md?t=1595931646412)
|
||||||
* [业务参数校验](files/10030_业务参数校验.md?t=1595832341001)
|
* [业务参数校验](files/10030_业务参数校验.md?t=1595931646412)
|
||||||
* [错误处理](files/10040_错误处理.md?t=1595832341001)
|
* [错误处理](files/10040_错误处理.md?t=1595931646412)
|
||||||
* [编写文档](files/10041_编写文档.md?t=1595832341001)
|
* [编写文档](files/10041_编写文档.md?t=1595931646412)
|
||||||
* [接口交互详解](files/10050_接口交互详解.md?t=1595832341001)
|
* [接口交互详解](files/10050_接口交互详解.md?t=1595931646412)
|
||||||
* [easyopen支持](files/10070_easyopen支持.md?t=1595832341001)
|
* [使用签名校验工具](files/10080_使用签名校验工具.md?t=1595931646413)
|
||||||
* [使用签名校验工具](files/10080_使用签名校验工具.md?t=1595832341001)
|
* [ISV管理](files/10085_ISV管理.md?t=1595931646413)
|
||||||
* [ISV管理](files/10085_ISV管理.md?t=1595832341001)
|
* [自定义返回结果](files/10087_自定义返回结果.md?t=1595931646413)
|
||||||
* [自定义返回结果](files/10087_自定义返回结果.md?t=1595832341001)
|
* [自定义过滤器](files/10088_自定义过滤器.md?t=1595931646413)
|
||||||
* [自定义过滤器](files/10088_自定义过滤器.md?t=1595832341001)
|
* [自定义校验token](files/10089_自定义校验token.md?t=1595931646413)
|
||||||
* [自定义校验token](files/10089_自定义校验token.md?t=1595832341002)
|
* [网关拦截器](files/10090_网关拦截器.md?t=1595931646413)
|
||||||
* [网关拦截器](files/10090_网关拦截器.md?t=1595832341002)
|
* [路由授权](files/10090_路由授权.md?t=1595931646413)
|
||||||
* [路由授权](files/10090_路由授权.md?t=1595832341002)
|
* [接口限流](files/10092_接口限流.md?t=1595931646413)
|
||||||
* [接口限流](files/10092_接口限流.md?t=1595832341002)
|
* [路由监控](files/10093_路由监控.md?t=1595931646413)
|
||||||
* [路由监控](files/10093_路由监控.md?t=1595832341002)
|
* [SDK开发](files/10095_SDK开发.md?t=1595931646413)
|
||||||
* [SDK开发](files/10095_SDK开发.md?t=1595832341002)
|
* [应用授权](files/10097_应用授权.md?t=1595931646414)
|
||||||
* [使用SpringCloudGateway](files/10096_使用SpringCloudGateway.md?t=1595832341002)
|
* [提供restful接口](files/10100_提供restful接口.md?t=1595931646414)
|
||||||
* [应用授权](files/10097_应用授权.md?t=1595832341003)
|
* [文件上传](files/10104_文件上传.md?t=1595931646414)
|
||||||
* [提供restful接口](files/10100_提供restful接口.md?t=1595832341003)
|
* [配置Sleuth链路追踪](files/10109_配置Sleuth链路追踪.md?t=1595931646414)
|
||||||
* [文件上传](files/10104_文件上传.md?t=1595832341003)
|
* [预发布灰度发布](files/10110_预发布灰度发布.md?t=1595931646414)
|
||||||
* [配置Sleuth链路追踪](files/10109_配置Sleuth链路追踪.md?t=1595832341003)
|
* [动态修改请求参数](files/10111_动态修改请求参数.md?t=1595931646414)
|
||||||
* [预发布灰度发布](files/10110_预发布灰度发布.md?t=1595832341003)
|
* [使用eureka](files/10112_使用eureka.md?t=1595931646414)
|
||||||
* [动态修改请求参数](files/10111_动态修改请求参数.md?t=1595832341003)
|
|
||||||
* [使用eureka](files/10112_使用eureka.md?t=1595832341003)
|
|
||||||
* [扩展其它注册中心](files/10113_扩展其它注册中心.md?t=1595832341003)
|
|
||||||
* 原理分析
|
* 原理分析
|
||||||
* [网关性能测试](files/90001_网关性能测试.md?t=1595832341004)
|
* [网关性能测试](files/90001_网关性能测试.md?t=1595931646414)
|
||||||
* [原理分析之@ApiMapping](files/90010_原理分析之@ApiMapping.md?t=1595832341004)
|
* [原理分析之如何存储路由](files/90011_原理分析之如何存储路由.md?t=1595931646415)
|
||||||
* [原理分析之如何存储路由](files/90011_原理分析之如何存储路由.md?t=1595832341004)
|
* [原理分析之如何路由](files/90012_原理分析之如何路由.md?t=1595931646415)
|
||||||
* [原理分析之如何路由](files/90012_原理分析之如何路由.md?t=1595832341004)
|
* [原理分析之文档归纳](files/90013_原理分析之文档归纳.md?t=1595931646415)
|
||||||
* [原理分析之文档归纳](files/90013_原理分析之文档归纳.md?t=1595832341004)
|
* [原理分析之预发布灰度发布](files/90014_原理分析之预发布灰度发布.md?t=1595931646415)
|
||||||
* [原理分析之预发布灰度发布](files/90014_原理分析之预发布灰度发布.md?t=1595832341004)
|
* [常见问题](files/90100_常见问题.md?t=1595931646415)
|
||||||
* [2.x升3.x注意事项](files/90099_2.x升3.x注意事项.md?t=1595832341004)
|
|
||||||
* [常见问题](files/90100_常见问题.md?t=1595832341004)
|
|
||||||
|
@@ -30,20 +30,3 @@
|
|||||||
- 修改sop-website下的application-dev.properties相关配置
|
- 修改sop-website下的application-dev.properties相关配置
|
||||||
- 运行WebsiteServerApplication.java
|
- 运行WebsiteServerApplication.java
|
||||||
- 访问http://localhost:8083
|
- 访问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
|
```java
|
||||||
@RestController
|
@RestController
|
||||||
public class StoryDemoController {
|
public class StoryDemoController {
|
||||||
|
|
||||||
|
@RequestMapping("/story/get")
|
||||||
|
public StoryResult getStory() {
|
||||||
|
StoryResult result = new StoryResult();
|
||||||
|
result.setId(1L);
|
||||||
|
result.setName("海底小纵队");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- 新增一个接口
|
只需要在方法上新增一个`@Open`注解,指定接口名即可
|
||||||
|
|
||||||
```java
|
```java
|
||||||
@ApiMapping(value = "story.demo.get")
|
// 添加一个@Open注解
|
||||||
|
@Open("story.demo.get")
|
||||||
|
@RequestMapping("/story/get")
|
||||||
public StoryResult getStory() {
|
public StoryResult getStory() {
|
||||||
StoryResult result = new StoryResult();
|
StoryResult result = new StoryResult();
|
||||||
result.setId(1L);
|
result.setId(1L);
|
||||||
@@ -24,13 +32,9 @@ public StoryResult getStory() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
这里的`@ApiMapping`注解作用同`@RequestMapping`注解,可以理解为是它的扩展
|
如果要加上版本号,指定`version`参数:`@Open(value = "story.demo.get", version = "2.0")`
|
||||||
|
|
||||||
value就是接口名,对应客户端的`method`参数
|
- 重启服务,这样接口就可以使用了。
|
||||||
|
|
||||||
如果要加上版本号,指定`version`参数:`@ApiMapping(value = "story.demo.get", version = "2.0")`
|
|
||||||
|
|
||||||
- 重启story服务,这样接口就可以使用了。
|
|
||||||
|
|
||||||
## 绑定业务参数
|
## 绑定业务参数
|
||||||
|
|
||||||
@@ -43,7 +47,8 @@ value就是接口名,对应客户端的`method`参数
|
|||||||
其中biz_content部分是我们想要的,在方法上申明一个对象,对应biz_content中的内容即可完成参数绑定,并且对参数进行JSR-303校验。
|
其中biz_content部分是我们想要的,在方法上申明一个对象,对应biz_content中的内容即可完成参数绑定,并且对参数进行JSR-303校验。
|
||||||
|
|
||||||
```java
|
```java
|
||||||
@ApiMapping(value = "goods.add")
|
@Open("goods.add")
|
||||||
|
@RequestMapping("/goods/add")
|
||||||
public Object addGoods(GoodsParam param) {
|
public Object addGoods(GoodsParam param) {
|
||||||
return param;
|
return param;
|
||||||
}
|
}
|
||||||
@@ -58,40 +63,14 @@ public class GoodsParam {
|
|||||||
一般情况下,只需要获取业务参数即可,如果想要获取更多的参数,可在后面跟一个`HttpServletRequest`对象。
|
一般情况下,只需要获取业务参数即可,如果想要获取更多的参数,可在后面跟一个`HttpServletRequest`对象。
|
||||||
|
|
||||||
```java
|
```java
|
||||||
@ApiMapping(value = "goods.add")
|
@Open("goods.add")
|
||||||
|
@RequestMapping("/goods/add")
|
||||||
public Object addGoods(GoodsParam param, HttpServletRequest request) {
|
public Object addGoods(GoodsParam param, HttpServletRequest request) {
|
||||||
System.out.println(request.getParameter("method"));
|
System.out.println(request.getParameter("method"));
|
||||||
return param;
|
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>();
|
Map<String, String> params = new HashMap<String, String>();
|
||||||
params.put("app_id", appId);
|
params.put("app_id", appId);
|
||||||
// 这里对应@ApiMapping.value属性
|
// 这里对应@Open.value属性
|
||||||
params.put("method", "story.demo.get");
|
params.put("method", "story.demo.get");
|
||||||
params.put("format", "json");
|
params.put("format", "json");
|
||||||
params.put("charset", "utf-8");
|
params.put("charset", "utf-8");
|
||||||
params.put("sign_type", "RSA2");
|
params.put("sign_type", "RSA2");
|
||||||
params.put("timestamp", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
|
params.put("timestamp", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
|
||||||
// 这里对应@ApiMapping.version属性
|
// 这里对应@Open.version属性
|
||||||
params.put("version", "1.0");
|
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=="}
|
{"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
|
在参数中使用注解即可,框架会自动进行验证。如下面一个添加商品接口,它的参数是GoodsParam
|
||||||
|
|
||||||
```java
|
```java
|
||||||
@ApiMapping(value = "goods.add")
|
@Open("goods.add")
|
||||||
|
@RequestMapping("/goods/add")
|
||||||
public void addGoods(GoodsParam param) {
|
public void addGoods(GoodsParam param) {
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
|
@@ -84,7 +84,7 @@ public class StoryResult {
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@ApiOperation(value = "获取故事信息", notes = "说明接口的详细信息,介绍,用途,注意事项等。")
|
@ApiOperation(value = "获取故事信息", notes = "说明接口的详细信息,介绍,用途,注意事项等。")
|
||||||
@ApiMapping(value = "alipay.story.find")
|
@Open(value = "alipay.story.find")
|
||||||
public StoryResult getStory2(StoryParam story) {
|
public StoryResult getStory2(StoryParam story) {
|
||||||
log.info("获取故事信息参数, story: {}", 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
|
```java
|
||||||
/**
|
/**
|
||||||
@@ -12,7 +12,9 @@ boolean needToken() default false;
|
|||||||
使用方式:
|
使用方式:
|
||||||
|
|
||||||
```java
|
```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) {
|
public StoryResult token(StoryParam story) {
|
||||||
OpenContext openContext = ServiceContext.getCurrentContext().getOpenContext();
|
OpenContext openContext = ServiceContext.getCurrentContext().getOpenContext();
|
||||||
String appAuthToken = openContext.getAppAuthToken();
|
String appAuthToken = openContext.getAppAuthToken();
|
||||||
@@ -42,13 +44,10 @@ public class MyConfig {
|
|||||||
ApiConfig.getInstance().setTokenValidator(apiParam -> {
|
ApiConfig.getInstance().setTokenValidator(apiParam -> {
|
||||||
// 获取客户端传递过来的token
|
// 获取客户端传递过来的token
|
||||||
String token = apiParam.fetchAccessToken();
|
String token = apiParam.fetchAccessToken();
|
||||||
if (StringUtils.isBlank(token)) {
|
return !StringUtils.isBlank(token);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// TODO: 校验token有效性,可以从redis中读取
|
// TODO: 校验token有效性,可以从redis中读取
|
||||||
|
|
||||||
// 返回true表示这个token真实、有效
|
// 返回true表示这个token真实、有效
|
||||||
return true;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -9,8 +9,8 @@
|
|||||||
|
|
||||||
假设把路由a,b,c分配给了`VIP角色`,那么具有VIP角色的ISV可以访问a,b,c三个路由。
|
假设把路由a,b,c分配给了`VIP角色`,那么具有VIP角色的ISV可以访问a,b,c三个路由。
|
||||||
|
|
||||||
默认情况下,接口访问时公开的,ISV都能访问。如果要设置某个接口访问权限,在`@ApiMapping`注解中指定permission=true。
|
默认情况下,接口访问时公开的,ISV都能访问。如果要设置某个接口访问权限,在`@Open`注解中指定permission=true。
|
||||||
如:`@ApiMapping(value = "permission.story.get", permission = true)`。这样该接口是需要经过授权给ISV才能访问的。
|
如:`@Open(value = "permission.story.get", permission = true)`。这样该接口是需要经过授权给ISV才能访问的。
|
||||||
|
|
||||||
重启服务后,登录admin,服务管理-路由列表界面中,操作操作列会出现一个授权按钮,点击出现授权窗口,勾选对应的角色即可完成授权。
|
重启服务后,登录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接口。
|
有些接口没有被开放,但是也想要通过网关来访问,SOP提供一个固定的请求格式来访问。
|
||||||
|
|
||||||
默认情况下提供restful功能是关闭的,开启方式如下:
|
|
||||||
|
|
||||||
- 打开sop-gateway配置文件,新增一行配置:
|
|
||||||
|
|
||||||
```properties
|
|
||||||
# 提供restful接口
|
|
||||||
sop.restful.enable=true
|
|
||||||
```
|
|
||||||
|
|
||||||
- 前端app请求网关(`2.4.1之后有变动`)
|
|
||||||
|
|
||||||
请求格式:
|
请求格式:
|
||||||
|
|
||||||
**2.4.1版本之前:** `http://ip:port/rest/your_path`,其中`http://ip:port/rest/`为固定部分,后面跟微服务请求路径。
|
`http://ip:port/rest/服务id/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`强制使用老的调用方式**
|
|
||||||
|
|
||||||
> 可在微服务端指定一个配置:`sop.restful.prefix=xxx`。请求路径将变成:`http://ip:port/rest/xxx/your_path`
|
> 可在微服务端指定一个配置:`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`
|
2. 本地访问:`http://10.0.1.22:2222/food/getFoodById/?id=2`
|
||||||
|
|
||||||
更多例子,可查看源码类:`TraditionalWebappController.java`
|
|
||||||
|
|
||||||
由此可见,对于前端调用者来说,它把网关看做一个大服务,只访问网关提供的请求,不需要关心网关后面的路由转发。网关后面各个微服务独自管理,
|
由此可见,对于前端调用者来说,它把网关看做一个大服务,只访问网关提供的请求,不需要关心网关后面的路由转发。网关后面各个微服务独自管理,
|
||||||
微服务之间的调用可以使用dubbo或feign,有了版本号的管理,可以做到服务的平滑升级,对用户来说都是无感知的。结合SOP-Admin提供的上下线功能,
|
微服务之间的调用可以使用dubbo或feign,有了版本号的管理,可以做到服务的平滑升级,对用户来说都是无感知的。结合SOP-Admin提供的上下线功能,
|
||||||
|
@@ -61,7 +61,8 @@ public UploadFile(String name, File file) throws IOException {
|
|||||||
* @param param
|
* @param param
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@ApiMapping(value = "demo.file.upload")
|
@Open("file.upload")
|
||||||
|
@RequestMapping("file1")
|
||||||
public FileUploadVO file1(FileUploadParam param) {
|
public FileUploadVO file1(FileUploadParam param) {
|
||||||
System.out.println(param.getRemark());
|
System.out.println(param.getRemark());
|
||||||
// 获取上传的文件
|
// 获取上传的文件
|
||||||
@@ -95,7 +96,8 @@ public class FileUploadParam {
|
|||||||
* @param param
|
* @param param
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@ApiMapping(value = "demo.file.upload2")
|
@Open("file.upload2")
|
||||||
|
@RequestMapping("file2")
|
||||||
public FileUploadVO file2(FileUploadParam2 param, HttpServletRequest request) {
|
public FileUploadVO file2(FileUploadParam2 param, HttpServletRequest request) {
|
||||||
System.out.println(param.getRemark());
|
System.out.println(param.getRemark());
|
||||||
FileUploadVO vo = new FileUploadVO();
|
FileUploadVO vo = new FileUploadVO();
|
||||||
|
@@ -21,8 +21,8 @@
|
|||||||
|
|
||||||
```properties
|
```properties
|
||||||
# nacos cloud配置
|
# nacos cloud配置
|
||||||
#spring.cloud.nacos.discovery.server-addr=${nacos.url}
|
#spring.cloud.nacos.discovery.server-addr=${register.url}
|
||||||
#nacos.config.server-addr=${nacos.url}
|
#nacos.config.server-addr=${register.url}
|
||||||
```
|
```
|
||||||
|
|
||||||
添加eureka配置
|
添加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
|
# https://blog.csdn.net/qq_36872046/article/details/81058045
|
||||||
# 路由转发超时时间,毫秒,默认值1000,详见:RibbonClientConfiguration.DEFAULT_READ_TIMEOUT。
|
# 路由转发超时时间,毫秒,默认值1000,详见:RibbonClientConfiguration.DEFAULT_READ_TIMEOUT。
|
||||||
# 如果微服务端 处理时间过长,会导致ribbon read超时,解决办法将这个值调大一点
|
# 如果微服务端 处理时间过长,会导致ribbon read超时,解决办法将这个值调大一点
|
||||||
ribbon.ReadTimeout: 60000
|
ribbon.ReadTimeout= 60000
|
||||||
```
|
```
|
||||||
|
|
||||||
## 指定了context-path(拉取路由404)
|
## 指定了context-path(拉取路由404)
|
||||||
|
2
pom.xml
2
pom.xml
@@ -51,7 +51,7 @@
|
|||||||
<!-- Test -->
|
<!-- Test -->
|
||||||
<junit.version>4.11</junit.version>
|
<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-io.version>2.5</commons-io.version>
|
||||||
<commons-fileupload.version>1.3.3</commons-fileupload.version>
|
<commons-fileupload.version>1.3.3</commons-fileupload.version>
|
||||||
<commons-collection.version>3.2.2</commons-collection.version>
|
<commons-collection.version>3.2.2</commons-collection.version>
|
||||||
|
@@ -22,8 +22,9 @@
|
|||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.alibaba.cloud</groupId>
|
<groupId>com.gitee.sop</groupId>
|
||||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
<artifactId>sop-bridge-nacos</artifactId>
|
||||||
|
<version>3.2.0-SNAPSHOT</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- easyopen starter -->
|
<!-- easyopen starter -->
|
||||||
|
@@ -10,4 +10,13 @@
|
|||||||
|
|
||||||
后台用户表:admin_user_info
|
后台用户表: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 adminLogin(LoginForm param) {
|
||||||
String username = param.getUsername();
|
String username = param.getUsername();
|
||||||
String password = param.getPassword();
|
String password = param.getPassword();
|
||||||
password = DigestUtils.md5Hex(password);
|
password = DigestUtils.md5Hex(username + password + username);
|
||||||
|
|
||||||
Query query = new Query()
|
Query query = new Query()
|
||||||
.eq("username", username)
|
.eq("username", username)
|
||||||
|
@@ -1,16 +1,29 @@
|
|||||||
package com.gitee.sop.adminserver.bean;
|
package com.gitee.sop.adminserver.bean;
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author tanghc
|
* @author tanghc
|
||||||
*/
|
*/
|
||||||
@Data
|
|
||||||
public class ServiceInfo {
|
public class ServiceInfo {
|
||||||
/** 服务名称 */
|
/** 服务名称 */
|
||||||
private String serviceId;
|
private String serviceId;
|
||||||
/** 实例列表 */
|
/** 实例列表 */
|
||||||
private List<ServiceInstance> instances;
|
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
|
sop.sign-type=rsa
|
||||||
|
|
||||||
# nacos配置
|
# nacos配置
|
||||||
nacos.config.server-addr=${nacos.url}
|
nacos.config.server-addr=${register.url}
|
||||||
nacos.discovery.server-addr=${nacos.url}
|
nacos.discovery.server-addr=${register.url}
|
||||||
|
|
||||||
# nacos cloud配置
|
# 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
|
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
|
||||||
|
@@ -5,7 +5,7 @@ mysql.host=localhost:3306
|
|||||||
mysql.username=root
|
mysql.username=root
|
||||||
mysql.password=root
|
mysql.password=root
|
||||||
|
|
||||||
# nacos注册中心地址
|
# 注册中心地址
|
||||||
nacos.url=127.0.0.1:8848
|
register.url=127.0.0.1:8848
|
||||||
|
|
||||||
logging.level.com.gitee=debug
|
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;
|
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.FetchTokenParam;
|
||||||
import com.gitee.sop.sopauth.auth.FetchTokenResult;
|
import com.gitee.sop.sopauth.auth.FetchTokenResult;
|
||||||
import com.gitee.sop.sopauth.auth.OAuth2Config;
|
import com.gitee.sop.sopauth.auth.OAuth2Config;
|
||||||
@@ -73,7 +73,8 @@ public class OAuth2Controller {
|
|||||||
* @param param
|
* @param param
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@ApiMapping("open.auth.token.app")
|
@Open("open.auth.token.app")
|
||||||
|
@RequestMapping("fetchToken")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public FetchTokenResult fetchToken(FetchTokenParam param) {
|
public FetchTokenResult fetchToken(FetchTokenParam param) {
|
||||||
FetchTokenResult fetchTokenResult = oAuth2Service.accessToken(param, OAuth2Config.getInstance());
|
FetchTokenResult fetchTokenResult = oAuth2Service.accessToken(param, OAuth2Config.getInstance());
|
||||||
|
@@ -8,12 +8,12 @@ mysql.username=root
|
|||||||
mysql.password=root
|
mysql.password=root
|
||||||
|
|
||||||
# nacos地址
|
# nacos地址
|
||||||
nacos.url=127.0.0.1:8848
|
register.url=127.0.0.1:8848
|
||||||
|
|
||||||
# ------- 需要改的配置end -------
|
# ------- 需要改的配置end -------
|
||||||
|
|
||||||
# nacos cloud配置
|
# 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
|
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
|
||||||
|
@@ -20,10 +20,10 @@
|
|||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<modules>
|
<modules>
|
||||||
|
<module>sop-bridge-nacos</module>
|
||||||
|
<module>sop-bridge-eureka</module>
|
||||||
<module>sop-gateway-common</module>
|
<module>sop-gateway-common</module>
|
||||||
<module>sop-service-common</module>
|
<module>sop-service-common</module>
|
||||||
<module>sop-bridge-zuul</module>
|
|
||||||
<module>sop-bridge-gateway</module>
|
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
|
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
# sop-common
|
# sop-common
|
||||||
|
|
||||||
- sop-bridge-gateway:网关桥接器,供sop-gateway依赖,依赖后使用spring cloud gateway网关
|
- sop-bridge-nacos:注册中心桥接器,接入nacos
|
||||||
- sop-bridge-zuul:网关桥接器,供sop-gateway依赖,依赖后使用spring cloud zuul网关
|
- sop-bridge-eureka:注册中心桥接器,接入eureka
|
||||||
- sop-gateway-common:提供给网关使用
|
- sop-gateway-common:提供给网关使用
|
||||||
- sop-service-common:提供给微服务端使用,需要打成jar
|
- sop-service-common:提供给微服务端使用,需要打成jar
|
||||||
|
|
||||||
|
@@ -12,7 +12,7 @@
|
|||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<version>3.2.0-SNAPSHOT</version>
|
<version>3.2.0-SNAPSHOT</version>
|
||||||
|
|
||||||
<artifactId>sop-bridge-zuul</artifactId>
|
<artifactId>sop-bridge-eureka</artifactId>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -23,11 +23,13 @@
|
|||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.cloud</groupId>
|
<groupId>org.springframework.cloud</groupId>
|
||||||
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
|
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.retry</groupId>
|
<groupId>org.springframework.cloud</groupId>
|
||||||
<artifactId>spring-retry</artifactId>
|
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</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.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.appinfo.InstanceInfo;
|
||||||
import com.netflix.discovery.shared.Application;
|
import com.netflix.discovery.shared.Application;
|
||||||
import com.netflix.discovery.shared.Applications;
|
import com.netflix.discovery.shared.Applications;
|
@@ -11,7 +11,7 @@
|
|||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<version>3.2.0-SNAPSHOT</version>
|
<version>3.2.0-SNAPSHOT</version>
|
||||||
|
|
||||||
<artifactId>sop-bridge-gateway</artifactId>
|
<artifactId>sop-bridge-nacos</artifactId>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -21,16 +21,14 @@
|
|||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.cloud</groupId>
|
<groupId>com.alibaba.cloud</groupId>
|
||||||
<artifactId>spring-cloud-starter-gateway</artifactId>
|
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.projectlombok</groupId>
|
||||||
<artifactId>spring-boot-starter-webflux</artifactId>
|
<artifactId>lombok</artifactId>
|
||||||
</dependency>
|
<scope>provided</scope>
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</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.cloud.nacos.NacosDiscoveryProperties;
|
||||||
import com.alibaba.nacos.api.exception.NacosException;
|
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.Instance;
|
||||||
import com.alibaba.nacos.api.naming.pojo.ListView;
|
import com.alibaba.nacos.api.naming.pojo.ListView;
|
||||||
import com.gitee.sop.gatewaycommon.bean.InstanceDefinition;
|
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 lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.ApplicationEvent;
|
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.alibaba.nacos.api.naming.pojo.Instance;
|
||||||
|
import com.gitee.sop.gatewaycommon.route.ServiceHolder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author tanghc
|
* @author tanghc
|
||||||
*/
|
*/
|
||||||
public class NacosServiceHolder extends ServiceHolder {
|
public class NacosServiceHolder extends ServiceHolder {
|
||||||
|
|
||||||
private Instance instance;
|
private final Instance instance;
|
||||||
|
|
||||||
public NacosServiceHolder(String serviceId, long lastUpdatedTimestamp, Instance instance) {
|
public NacosServiceHolder(String serviceId, long lastUpdatedTimestamp, Instance instance) {
|
||||||
super(serviceId, lastUpdatedTimestamp);
|
super(serviceId, lastUpdatedTimestamp);
|
@@ -7,7 +7,7 @@ sop.secret=MZZOUSTua6LzApIWXCwEgbBmxSzpzC
|
|||||||
sop.gateway-index-path=/
|
sop.gateway-index-path=/
|
||||||
|
|
||||||
# nacos cloud配置
|
# 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
|
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>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.google.guava</groupId>
|
<groupId>com.google.guava</groupId>
|
||||||
<artifactId>guava</artifactId>
|
<artifactId>guava</artifactId>
|
||||||
@@ -60,12 +61,6 @@
|
|||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.cloud</groupId>
|
|
||||||
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
|
|
||||||
<optional>true</optional>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.cloud</groupId>
|
<groupId>org.springframework.cloud</groupId>
|
||||||
<artifactId>spring-cloud-starter-gateway</artifactId>
|
<artifactId>spring-cloud-starter-gateway</artifactId>
|
||||||
@@ -76,11 +71,6 @@
|
|||||||
<artifactId>spring-boot-starter-webflux</artifactId>
|
<artifactId>spring-boot-starter-webflux</artifactId>
|
||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.cloud</groupId>
|
|
||||||
<artifactId>spring-cloud-netflix-eureka-server</artifactId>
|
|
||||||
<optional>true</optional>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.data</groupId>
|
<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.DefaultDataNameBuilder;
|
||||||
import com.gitee.sop.gatewaycommon.result.ResultAppender;
|
import com.gitee.sop.gatewaycommon.result.ResultAppender;
|
||||||
import com.gitee.sop.gatewaycommon.result.ResultExecutorForGateway;
|
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.CacheIsvManager;
|
||||||
import com.gitee.sop.gatewaycommon.secret.IsvManager;
|
import com.gitee.sop.gatewaycommon.secret.IsvManager;
|
||||||
import com.gitee.sop.gatewaycommon.session.ApiSessionManager;
|
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.Signer;
|
||||||
import com.gitee.sop.gatewaycommon.validate.TokenValidator;
|
import com.gitee.sop.gatewaycommon.validate.TokenValidator;
|
||||||
import com.gitee.sop.gatewaycommon.validate.Validator;
|
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 lombok.Data;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
@@ -67,11 +63,6 @@ public class ApiConfig {
|
|||||||
*/
|
*/
|
||||||
private ResultExecutorForGateway gatewayResultExecutor = new GatewayResultExecutor();
|
private ResultExecutorForGateway gatewayResultExecutor = new GatewayResultExecutor();
|
||||||
|
|
||||||
/**
|
|
||||||
* zuul合并结果处理
|
|
||||||
*/
|
|
||||||
private ResultExecutorForZuul zuulResultExecutor = new ZuulResultExecutor();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* isv管理
|
* isv管理
|
||||||
*/
|
*/
|
||||||
@@ -87,11 +78,6 @@ public class ApiConfig {
|
|||||||
*/
|
*/
|
||||||
private Signer signer = new ApiSigner();
|
private Signer signer = new ApiSigner();
|
||||||
|
|
||||||
/**
|
|
||||||
* 参数解析,zuul
|
|
||||||
*/
|
|
||||||
private ZuulParamBuilder zuulParamBuilder = new ZuulParamBuilder();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 验证
|
* 验证
|
||||||
*/
|
*/
|
||||||
@@ -147,11 +133,6 @@ public class ApiConfig {
|
|||||||
*/
|
*/
|
||||||
private ServiceErrorManager serviceErrorManager = new DefaultServiceErrorManager();
|
private ServiceErrorManager serviceErrorManager = new DefaultServiceErrorManager();
|
||||||
|
|
||||||
/**
|
|
||||||
* zuul网关全局异常处理
|
|
||||||
*/
|
|
||||||
private ZuulErrorController zuulErrorController = new ZuulErrorController();
|
|
||||||
|
|
||||||
private ParameterFormatter parameterFormatter;
|
private ParameterFormatter parameterFormatter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -52,5 +52,6 @@ public class SopConstants {
|
|||||||
|
|
||||||
public static final String CACHE_ROUTE_INTERCEPTOR_CONTEXT = "cacheRouteInterceptorContext";
|
public static final String CACHE_ROUTE_INTERCEPTOR_CONTEXT = "cacheRouteInterceptorContext";
|
||||||
public static final String TARGET_SERVICE = "sop-target-service";
|
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.ApiConfig;
|
||||||
import com.gitee.sop.gatewaycommon.bean.ApiContext;
|
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.MonitorRouteInterceptor;
|
||||||
import com.gitee.sop.gatewaycommon.interceptor.RouteInterceptor;
|
import com.gitee.sop.gatewaycommon.interceptor.RouteInterceptor;
|
||||||
import com.gitee.sop.gatewaycommon.limit.LimitManager;
|
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.message.ErrorFactory;
|
||||||
import com.gitee.sop.gatewaycommon.param.ParameterFormatter;
|
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.RegistryListener;
|
||||||
import com.gitee.sop.gatewaycommon.route.ServiceListener;
|
import com.gitee.sop.gatewaycommon.route.ServiceListener;
|
||||||
import com.gitee.sop.gatewaycommon.route.ServiceRouteListener;
|
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.ConditionalOnMissingBean;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
import org.springframework.cloud.client.discovery.event.HeartbeatEvent;
|
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.ServerIntrospector;
|
||||||
import org.springframework.cloud.netflix.ribbon.eureka.EurekaServerIntrospector;
|
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.context.ApplicationContextAware;
|
import org.springframework.context.ApplicationContextAware;
|
||||||
import org.springframework.context.ApplicationEvent;
|
import org.springframework.context.ApplicationEvent;
|
||||||
@@ -39,9 +42,7 @@ import org.springframework.context.annotation.Bean;
|
|||||||
import org.springframework.context.event.EventListener;
|
import org.springframework.context.event.EventListener;
|
||||||
import org.springframework.core.env.Environment;
|
import org.springframework.core.env.Environment;
|
||||||
import org.springframework.web.cors.CorsConfiguration;
|
import org.springframework.web.cors.CorsConfiguration;
|
||||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
|
||||||
import org.springframework.web.cors.reactive.CorsWebFilter;
|
import org.springframework.web.cors.reactive.CorsWebFilter;
|
||||||
import org.springframework.web.filter.CorsFilter;
|
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -96,27 +97,6 @@ public class AbstractConfiguration implements ApplicationContextAware, Applicati
|
|||||||
registryListener.onEvent(heartbeatEvent);
|
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
|
@Bean
|
||||||
@ConditionalOnMissingBean
|
@ConditionalOnMissingBean
|
||||||
ServiceListener serviceListener() {
|
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
|
* 跨域过滤器,gateway采用react形式,需要使用reactive包下的UrlBasedCorsConfigurationSource
|
||||||
*/
|
*/
|
||||||
@@ -226,15 +194,7 @@ public class AbstractConfiguration implements ApplicationContextAware, Applicati
|
|||||||
return new NacosServerIntrospector();
|
return new NacosServerIntrospector();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 负责获取eureka实例的metadata
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
@Bean
|
|
||||||
@ConditionalOnProperty("eureka.client.serviceUrl.defaultZone")
|
|
||||||
ServerIntrospector eurekaServerIntrospector() {
|
|
||||||
return new EurekaServerIntrospector();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run(ApplicationArguments args) throws Exception {
|
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 com.gitee.sop.gatewaycommon.gateway.configuration.AlipayGatewayConfiguration;
|
||||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||||
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
|
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
|
@@ -20,7 +20,7 @@ public class SopGatewayEnvironmentPostProcessor implements EnvironmentPostProces
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
|
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
|
||||||
Resource resource = new ClassPathResource("sop-bridge.properties");
|
Resource resource = new ClassPathResource("META-INF/gateway.properties");
|
||||||
// 加载成PropertySource对象,并添加到Environment环境中
|
// 加载成PropertySource对象,并添加到Environment环境中
|
||||||
environment.getPropertySources().addLast(loadProfiles(resource));
|
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);
|
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);
|
int index = path.indexOf(REST_PATH);
|
||||||
// 取"/rest"的后面部分
|
// 取"/rest"的后面部分
|
||||||
String newPath = path.substring(index + REST_PATH.length());
|
return path.substring(index + REST_PATH.length());
|
||||||
ApiParam apiParam = ApiParam.createRestfulApiParam(newPath);
|
|
||||||
setApiParam(exchange, apiParam);
|
|
||||||
return apiParam;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -97,7 +94,7 @@ public class ServerWebExchangeUtil {
|
|||||||
* @param forwardPath 重定向path
|
* @param forwardPath 重定向path
|
||||||
* @return 返回新的ServerWebExchange,配合chain.filter(newExchange);使用
|
* @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()
|
ServerHttpRequest newRequest = exchange.getRequest()
|
||||||
.mutate()
|
.mutate()
|
||||||
.path(forwardPath).build();
|
.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.GatewayForwardChooser;
|
||||||
import com.gitee.sop.gatewaycommon.gateway.route.GatewayRouteCache;
|
import com.gitee.sop.gatewaycommon.gateway.route.GatewayRouteCache;
|
||||||
import com.gitee.sop.gatewaycommon.gateway.route.GatewayRouteRepository;
|
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 com.gitee.sop.gatewaycommon.manager.RouteRepositoryContext;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.ObjectProvider;
|
import org.springframework.beans.factory.ObjectProvider;
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
package com.gitee.sop.gatewaycommon.gateway.filter;
|
package com.gitee.sop.gatewaycommon.gateway.filter;
|
||||||
|
|
||||||
import com.gitee.sop.gatewaycommon.bean.ApiContext;
|
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.result.ResultExecutor;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.reactivestreams.Publisher;
|
import org.reactivestreams.Publisher;
|
||||||
@@ -48,6 +49,10 @@ public class GatewayModifyResponseGatewayFilter implements GlobalFilter, Ordered
|
|||||||
if (StringUtils.containsIgnoreCase(originalResponseContentType, MediaType.APPLICATION_OCTET_STREAM_VALUE)) {
|
if (StringUtils.containsIgnoreCase(originalResponseContentType, MediaType.APPLICATION_OCTET_STREAM_VALUE)) {
|
||||||
return chain.filter(exchange);
|
return chain.filter(exchange);
|
||||||
}
|
}
|
||||||
|
// rest请求,直接放行
|
||||||
|
if (exchange.getAttribute(SopConstants.RESTFUL_REQUEST) != null) {
|
||||||
|
return chain.filter(exchange);
|
||||||
|
}
|
||||||
Class inClass = String.class;
|
Class inClass = String.class;
|
||||||
Class outClass = String.class;
|
Class outClass = String.class;
|
||||||
HttpHeaders httpHeaders = new HttpHeaders();
|
HttpHeaders httpHeaders = new HttpHeaders();
|
||||||
|
@@ -48,7 +48,7 @@ public class IndexFilter implements WebFilter {
|
|||||||
private static final String REST_PATH_PREFIX = "/rest";
|
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"
|
"/sop", "/actuator"
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -71,15 +71,9 @@ public class IndexFilter implements WebFilter {
|
|||||||
}
|
}
|
||||||
// 如果是restful请求,直接转发
|
// 如果是restful请求,直接转发
|
||||||
if (path.startsWith(REST_PATH_PREFIX)) {
|
if (path.startsWith(REST_PATH_PREFIX)) {
|
||||||
String sopRestfulEnableValue = EnvironmentKeys.SOP_RESTFUL_ENABLE.getValue();
|
exchange.getAttributes().put(SopConstants.RESTFUL_REQUEST, true);
|
||||||
if (!Objects.equals("true", sopRestfulEnableValue)) {
|
String restfulPath = ServerWebExchangeUtil.getRestfulPath(path);
|
||||||
log.error("尝试调用restful请求,但sop.restful.enable未开启");
|
ServerWebExchange newExchange = ServerWebExchangeUtil.getForwardExchange(exchange, restfulPath);
|
||||||
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);
|
|
||||||
return chain.filter(newExchange);
|
return chain.filter(newExchange);
|
||||||
}
|
}
|
||||||
if (Objects.equals(path, indexPath)) {
|
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.ApiConfig;
|
||||||
import com.gitee.sop.gatewaycommon.bean.ConfigLimitDto;
|
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.exception.ApiException;
|
||||||
import com.gitee.sop.gatewaycommon.gateway.ServerWebExchangeUtil;
|
import com.gitee.sop.gatewaycommon.gateway.ServerWebExchangeUtil;
|
||||||
import com.gitee.sop.gatewaycommon.limit.LimitManager;
|
import com.gitee.sop.gatewaycommon.limit.LimitManager;
|
||||||
@@ -32,12 +33,18 @@ public class LimitFilter implements GlobalFilter, Ordered {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||||
|
if (exchange.getAttribute(SopConstants.RESTFUL_REQUEST) != null) {
|
||||||
|
return chain.filter(exchange);
|
||||||
|
}
|
||||||
ApiConfig apiConfig = ApiConfig.getInstance();
|
ApiConfig apiConfig = ApiConfig.getInstance();
|
||||||
// 限流功能未开启,直接返回
|
// 限流功能未开启,直接返回
|
||||||
if (!apiConfig.isOpenLimit()) {
|
if (!apiConfig.isOpenLimit()) {
|
||||||
return chain.filter(exchange);
|
return chain.filter(exchange);
|
||||||
}
|
}
|
||||||
ApiParam apiParam = ServerWebExchangeUtil.getApiParam(exchange);
|
ApiParam apiParam = ServerWebExchangeUtil.getApiParam(exchange);
|
||||||
|
if (apiParam == null) {
|
||||||
|
return chain.filter(exchange);
|
||||||
|
}
|
||||||
ConfigLimitDto configLimitDto = this.findConfigLimitDto(apiConfig, apiParam, exchange);
|
ConfigLimitDto configLimitDto = this.findConfigLimitDto(apiConfig, apiParam, exchange);
|
||||||
if (configLimitDto == null) {
|
if (configLimitDto == null) {
|
||||||
return chain.filter(exchange);
|
return chain.filter(exchange);
|
||||||
|
@@ -3,10 +3,10 @@ package com.gitee.sop.gatewaycommon.gateway.result;
|
|||||||
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSON;
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
import com.gitee.sop.gatewaycommon.bean.DefaultRouteInterceptorContext;
|
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.bean.SopConstants;
|
||||||
import com.gitee.sop.gatewaycommon.exception.ApiException;
|
import com.gitee.sop.gatewaycommon.exception.ApiException;
|
||||||
import com.gitee.sop.gatewaycommon.gateway.ServerWebExchangeUtil;
|
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.Error;
|
||||||
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
|
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
|
||||||
import com.gitee.sop.gatewaycommon.param.ApiParam;
|
import com.gitee.sop.gatewaycommon.param.ApiParam;
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
package com.gitee.sop.gatewaycommon.zuul.loadbalancer;
|
package com.gitee.sop.gatewaycommon.loadbalancer;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
@@ -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;
|
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 com.google.common.collect.Maps;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -13,12 +13,12 @@ public class DefaultEnvGrayManager implements EnvGrayManager {
|
|||||||
/**
|
/**
|
||||||
* key:serviceId,服务对应的灰度配置
|
* key:serviceId,服务对应的灰度配置
|
||||||
*/
|
*/
|
||||||
private Map<String, ServiceGrayConfig> serviceGrayConfigMap = Maps.newConcurrentMap();
|
private final Map<String, ServiceGrayConfig> serviceGrayConfigMap = Maps.newConcurrentMap();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* key:instanceId value:serviceId
|
* key:instanceId value:serviceId
|
||||||
*/
|
*/
|
||||||
private Map<String, String> instanceIdServiceIdMap = Maps.newConcurrentMap();
|
private final Map<String, String> instanceIdServiceIdMap = Maps.newConcurrentMap();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void saveServiceGrayConfig(ServiceGrayConfig serviceGrayConfig) {
|
public void saveServiceGrayConfig(ServiceGrayConfig serviceGrayConfig) {
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
package com.gitee.sop.gatewaycommon.manager;
|
package com.gitee.sop.gatewaycommon.manager;
|
||||||
|
|
||||||
import com.gitee.sop.gatewaycommon.bean.BeanInitializer;
|
import com.gitee.sop.gatewaycommon.bean.BeanInitializer;
|
||||||
import com.gitee.sop.gatewaycommon.zuul.loadbalancer.ServiceGrayConfig;
|
import com.gitee.sop.gatewaycommon.loadbalancer.ServiceGrayConfig;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author tanghc
|
* @author tanghc
|
||||||
|
@@ -1,28 +1,17 @@
|
|||||||
package com.gitee.sop.gatewaycommon.manager;
|
package com.gitee.sop.gatewaycommon.manager;
|
||||||
|
|
||||||
import com.gitee.sop.gatewaycommon.zuul.loadbalancer.EnvironmentServerChooser;
|
|
||||||
|
|
||||||
public enum EnvironmentKeys {
|
public enum EnvironmentKeys {
|
||||||
SPRING_PROFILES_ACTIVE("spring.profiles.active", "default"),
|
SPRING_PROFILES_ACTIVE("spring.profiles.active", "default"),
|
||||||
/**
|
/**
|
||||||
* spring.application.name
|
* spring.application.name
|
||||||
*/
|
*/
|
||||||
SPRING_APPLICATION_NAME("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=true,签名验证拼接字符串的value部分进行urlencode
|
||||||
*/
|
*/
|
||||||
SIGN_URLENCODE("sign.urlencode"),
|
SIGN_URLENCODE("sign.urlencode"),
|
||||||
|
|
||||||
/**
|
|
||||||
* sop.restful.enable=true,开启传统web开发模式
|
|
||||||
*/
|
|
||||||
SOP_RESTFUL_ENABLE("sop.restful.enable"),
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* sop.restful.path=/xx ,指定请求前缀,默认/rest
|
* 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;
|
private String defaultValue;
|
||||||
|
|
||||||
public String getKey() {
|
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.alibaba.fastjson.JSON;
|
||||||
import com.gitee.sop.gatewaycommon.bean.InstanceDefinition;
|
import com.gitee.sop.gatewaycommon.bean.InstanceDefinition;
|
||||||
import com.gitee.sop.gatewaycommon.bean.ServiceRouteInfo;
|
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.BaseRouteCache;
|
||||||
import com.gitee.sop.gatewaycommon.manager.EnvironmentKeys;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@@ -46,7 +44,7 @@ public class ServiceRouteListener extends BaseServiceListener {
|
|||||||
String serviceName = instance.getServiceId();
|
String serviceName = instance.getServiceId();
|
||||||
String url = getRouteRequestUrl(instance);
|
String url = getRouteRequestUrl(instance);
|
||||||
log.info("拉取路由配置,serviceId: {}, url: {}", serviceName, url);
|
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) {
|
if (responseEntity.getStatusCode() == HttpStatus.OK) {
|
||||||
String body = responseEntity.getBody();
|
String body = responseEntity.getBody();
|
||||||
ServiceRouteInfo serviceRouteInfo = JSON.parseObject(body, ServiceRouteInfo.class);
|
ServiceRouteInfo serviceRouteInfo = JSON.parseObject(body, ServiceRouteInfo.class);
|
||||||
@@ -58,9 +56,6 @@ public class ServiceRouteListener extends BaseServiceListener {
|
|||||||
|
|
||||||
protected HttpEntity<String> getHttpEntity() {
|
protected HttpEntity<String> getHttpEntity() {
|
||||||
HttpHeaders headers = new HttpHeaders();
|
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);
|
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.bean.SopConstants;
|
||||||
import com.gitee.sop.gatewaycommon.param.ApiUploadContext;
|
import com.gitee.sop.gatewaycommon.param.ApiUploadContext;
|
||||||
import com.gitee.sop.gatewaycommon.param.UploadContext;
|
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 lombok.Data;
|
||||||
import org.apache.commons.codec.digest.DigestUtils;
|
import org.apache.commons.codec.digest.DigestUtils;
|
||||||
import org.apache.commons.fileupload.FileItem;
|
import org.apache.commons.fileupload.FileItem;
|
||||||
@@ -16,8 +14,6 @@ import org.apache.commons.lang3.StringUtils;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.http.HttpHeaders;
|
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.http.server.reactive.ServerHttpRequest;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
import org.springframework.web.multipart.commons.CommonsMultipartFile;
|
import org.springframework.web.multipart.commons.CommonsMultipartFile;
|
||||||
@@ -121,9 +117,6 @@ public class RequestUtil {
|
|||||||
* @return 返回参数键值对
|
* @return 返回参数键值对
|
||||||
*/
|
*/
|
||||||
public static Map<String, String> convertRequestParamsToMap(HttpServletRequest request) {
|
public static Map<String, String> convertRequestParamsToMap(HttpServletRequest request) {
|
||||||
if (request instanceof HttpServletRequestWrapper) {
|
|
||||||
request = ((HttpServletRequestWrapper) request).getRequest();
|
|
||||||
}
|
|
||||||
Map<String, String[]> paramMap = request.getParameterMap();
|
Map<String, String[]> paramMap = request.getParameterMap();
|
||||||
if (paramMap == null || paramMap.isEmpty()) {
|
if (paramMap == null || paramMap.isEmpty()) {
|
||||||
return Collections.emptyMap();
|
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
|
@Data
|
||||||
public static class UploadInfo {
|
public static class UploadInfo {
|
||||||
private Map<String, String> uploadParams;
|
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();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* key:nameVersion
|
|
||||||
*/
|
|
||||||
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;
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置接口能力。
|
|
||||||
* 如果想把已经存在的接口开放出去,可用此注解。<br>
|
|
||||||
* 作用于Controller类上或方法上。如果作用在类上,则类中的所有方法将具备开放平台接口提供能力。
|
|
||||||
* @author tanghc
|
* @author tanghc
|
||||||
*/
|
*/
|
||||||
@Target({ElementType.METHOD, ElementType.TYPE})
|
@Target(ElementType.METHOD)
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Documented
|
@Documented
|
||||||
public @interface ApiAbility {
|
public @interface Open {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 版本号,如:1.0
|
* 接口名,如:member.user.get
|
||||||
|
*/
|
||||||
|
String value();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 版本号,默认版本号是""<br>
|
||||||
|
* 改默认版本号:<code>ServiceConfig.getInstance().setDefaultVersion("1.0");</code>
|
||||||
*/
|
*/
|
||||||
String version() default "";
|
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<GoodsParam> 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;
|
private int permission;
|
||||||
/** 是否需要token */
|
/** 是否需要token */
|
||||||
private int needToken;
|
private int needToken;
|
||||||
/** 是否是兼容模式,即使用了@ApiAbility注解 */
|
|
||||||
private int compatibleMode;
|
|
||||||
/** 是否是原始Mapping */
|
|
||||||
private boolean originalMapping;
|
|
||||||
|
|
||||||
public ApiMeta() {
|
public ApiMeta() {
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user