# Conflicts: # sop-admin/sop-admin-server/pom.xml # sop-admin/sop-admin-server/src/main/resources/application-dev.properties # sop-auth/src/main/resources/application-dev.properties # sop-common/sop-bridge-nacos/src/main/resources/sop-bridge.properties # sop-common/sop-gateway-common/src/main/resources/sop-bridge.properties # sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/configuration/BaseServiceConfiguration.java # sop-example/sop-story/pom.xml # sop-example/sop-story/src/main/resources/application-dev.properties # sop-gateway/pom.xml # sop-gateway/src/main/resources/application-dev.properties # sop-website/pom.xml # sop-website/src/main/resources/application-dev.propertieseureka
commit
2e4450b5ed
@ -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) |
|
||||||
|
@ -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,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,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中,如果这个功能没有用到,可以放心升级。 |
|
@ -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; |
||||||
|
} |
||||||
} |
} |
||||||
|
@ -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); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -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; |
@ -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,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); |
@ -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 { |
|
||||||
} |
|
||||||
|
|
@ -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; |
@ -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; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
} |
|
@ -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,9 +0,0 @@ |
|||||||
package com.gitee.sop.gatewaycommon.result; |
|
||||||
|
|
||||||
import com.netflix.zuul.context.RequestContext; |
|
||||||
|
|
||||||
/** |
|
||||||
* @author tanghc |
|
||||||
*/ |
|
||||||
public interface ResultExecutorForZuul extends ResultExecutor<RequestContext, String> { |
|
||||||
} |
|
@ -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); |
|
||||||
} |
|
||||||
} |
|
@ -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 {}; |
|
||||||
} |
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue