tanghc 6 years ago
parent 11ea3c2ae9
commit 7d22c92122
  1. 12
      sop-gateway-common/pom.xml
  2. 28
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ApiConfig.java
  3. 6
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ApiContext.java
  4. 24
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/BaseRouteDefinition.java
  5. 12
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/BaseServiceRouteInfo.java
  6. 24
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/SopConstants.java
  7. 9
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/GatewayContext.java
  8. 2
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/configuration/AlipayGatewayConfiguration.java
  9. 44
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/configuration/BaseGatewayConfiguration.java
  10. 2
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/configuration/TaobaoGatewayConfiguration.java
  11. 6
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/GatewayModifyResponseGatewayFilter.java
  12. 2
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/LoadBalancerClientExtFilter.java
  13. 4
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/ValidateFilter.java
  14. 12
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/handler/GatewayExceptionHandler.java
  15. 6
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/param/GatewayParamBuilder.java
  16. 2
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/result/GatewayResult.java
  17. 70
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/result/GatewayResultExecutor.java
  18. 2
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/GatewayContext.java
  19. 6
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/GatewayFilterDefinition.java
  20. 2
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/GatewayPredicateDefinition.java
  21. 16
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/GatewayRouteDefinition.java
  22. 14
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/GatewayRouteRepository.java
  23. 12
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/GatewayServiceRouteInfo.java
  24. 65
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/GatewayZookeeperRouteManager.java
  25. 2
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/NameVersionRoutePredicateFactory.java
  26. 2
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/ReadBodyRoutePredicateFactory.java
  27. 20
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/ApiMetaConfig.java
  28. 22
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/ApiMetaManager.java
  29. 135
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/BaseRouteManager.java
  30. 168
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/GatewayZookeeperApiMetaManager.java
  31. 16
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/RouteManager.java
  32. 14
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/RouteRepository.java
  33. 8
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/param/ParamBuilder.java
  34. 146
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/ApiResultExecutor.java
  35. 82
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/BaseExecutorAdapter.java
  36. 40
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/ResultExecutor.java
  37. 12
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/ServiceRouteInfo.java
  38. 37
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/RequestUtil.java
  39. 167
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/ZuulContext.java
  40. 16
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/configuration/AlipayZuulConfiguration.java
  41. 92
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/configuration/BaseZuulConfiguration.java
  42. 43
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/configuration/BaseZuulController.java
  43. 23
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/configuration/TaobaoZuulConfiguration.java
  44. 117
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/filter/BaseZuulFilter.java
  45. 60
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/filter/ErrorFilter.java
  46. 48
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/filter/PostResultFilter.java
  47. 41
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/filter/PreTokenFilter.java
  48. 48
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/filter/PreValidateFilter.java
  49. 111
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/param/ZuulParamBuilder.java
  50. 58
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/result/ZuulResultExecutor.java
  51. 46
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/route/SopRouteLocator.java
  52. 14
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/route/ZuulRouteDefinition.java
  53. 41
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/route/ZuulRouteRepository.java
  54. 12
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/route/ZuulServiceRouteInfo.java
  55. 33
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/route/ZuulZookeeperRouteManager.java
  56. 27
      sop-gateway/pom.xml
  57. 33
      sop-gateway/src/main/java/com/gitee/sop/gateway/config/GatewayConfig.java
  58. 16
      sop-story/sop-story-web/src/main/java/com/gitee/sop/bookweb/config/OpenServiceConfig.java

@ -45,18 +45,20 @@
</properties>
<dependencies>
<!--<dependency>-->
<!--<groupId>org.springframework.cloud</groupId>-->
<!--<artifactId>spring-cloud-starter-netflix-zuul</artifactId>-->
<!--</dependency>-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>

@ -1,8 +1,9 @@
package com.gitee.sop.gatewaycommon.bean;
import com.gitee.sop.gatewaycommon.param.ApiParamBuilder;
import com.gitee.sop.gatewaycommon.gateway.param.GatewayParamBuilder;
import com.gitee.sop.gatewaycommon.gateway.result.GatewayResult;
import com.gitee.sop.gatewaycommon.param.ParamBuilder;
import com.gitee.sop.gatewaycommon.result.ApiResultExecutor;
import com.gitee.sop.gatewaycommon.gateway.result.GatewayResultExecutor;
import com.gitee.sop.gatewaycommon.result.ResultExecutor;
import com.gitee.sop.gatewaycommon.secret.AppSecretManager;
import com.gitee.sop.gatewaycommon.secret.CacheAppSecretManager;
@ -14,7 +15,12 @@ import com.gitee.sop.gatewaycommon.validate.ApiValidator;
import com.gitee.sop.gatewaycommon.validate.Encrypter;
import com.gitee.sop.gatewaycommon.validate.Signer;
import com.gitee.sop.gatewaycommon.validate.Validator;
import com.gitee.sop.gatewaycommon.zuul.configuration.BaseZuulController;
import com.gitee.sop.gatewaycommon.zuul.param.ZuulParamBuilder;
import com.gitee.sop.gatewaycommon.zuul.result.ZuulResultExecutor;
import com.netflix.zuul.context.RequestContext;
import lombok.Data;
import org.springframework.web.server.ServerWebExchange;
import java.util.ArrayList;
import java.util.List;
@ -32,9 +38,14 @@ public class ApiConfig {
}
/**
* 合并结果处理
* gateway合并结果处理
*/
private ResultExecutor resultExecutor = new ApiResultExecutor();
private ResultExecutor<ServerWebExchange, GatewayResult> gatewayResultExecutor = new GatewayResultExecutor();
/**
* zuul合并结果处理
*/
private ResultExecutor<RequestContext, String> zuulResultExecutor = new ZuulResultExecutor();
/**
* app秘钥管理
@ -54,7 +65,12 @@ public class ApiConfig {
/**
* 参数解析gateway
*/
private ParamBuilder paramBuilder = new ApiParamBuilder();
private ParamBuilder<ServerWebExchange> gatewayParamBuilder = new GatewayParamBuilder();
/**
* 参数解析zuul
*/
private ParamBuilder<RequestContext> zuulParamBuilder = new ZuulParamBuilder();
/**
* 验证
@ -66,6 +82,8 @@ public class ApiConfig {
*/
private SessionManager sessionManager = new ApiSessionManager();
private BaseZuulController baseZuulController = new BaseZuulController();
/**
* 错误模块
*/

@ -2,8 +2,6 @@ package com.gitee.sop.gatewaycommon.bean;
import com.gitee.sop.gatewaycommon.session.SessionManager;
//import com.netflix.zuul.context.RequestContext;
/**
* 应用上下文,方便获取信息
*
@ -11,10 +9,6 @@ import com.gitee.sop.gatewaycommon.session.SessionManager;
*/
public class ApiContext {
private ApiContext() {
}
/**
* 获取session管理器
*

@ -0,0 +1,24 @@
package com.gitee.sop.gatewaycommon.bean;
import lombok.Getter;
import lombok.Setter;
/**
* @author tanghc
*/
@Getter
@Setter
public class BaseRouteDefinition {
/**
* 路由的Id
*/
private String id;
/**
* 路由规则转发的目标uri
*/
private String uri;
/**
* 路由执行的顺序
*/
private int order = 0;
}

@ -0,0 +1,12 @@
package com.gitee.sop.gatewaycommon.bean;
import lombok.Data;
import java.util.List;
@Data
public class BaseServiceRouteInfo<T extends BaseRouteDefinition> {
private String appName;
private String md5;
private List<T> routeDefinitionList;
}

@ -8,29 +8,11 @@ import java.nio.charset.StandardCharsets;
*/
public class SopConstants {
public static final String RANDOM_KEY_NAME = "ssl_randomKey";
public static final Charset CHARSET_UTF8 = StandardCharsets.UTF_8;
public static final String UTF8 = "UTF-8";
public static final String FORMAT_JSON = "json";
public static final String FORMAT_XML = "xml";
public static final String AUTHORIZATION = "Authorization";
public static final String PREFIX_BEARER = "Bearer ";
public static final String YMDHMS = "yyyy-MM-dd HH:mm:ss";
public static final String DEFAULT_SIGN_METHOD = "md5";
public static final String CONTENT_TYPE_NAME = "Content-Type";
public static final String CONTENT_TYPE_JSON = "application/json;charset=UTF-8";
public static final String LINE = "\n";
public static final String EMPTY_JSON = "{}";
public static final String SORT_DESC = "DESC";
public static final String REST_PARAM_NAME = "_REST_PARAM_NAME_";
public static final String REST_PARAM_VERSION = "_REST_PARAM_VERSION_";
/**
* 在拦截器中调用获取参数
* String cachedBody = (String)exchange.getAttribute(SopConstants.CACHE_REQUEST_BODY_OBJECT_KEY);
@ -47,4 +29,10 @@ public class SopConstants {
public static final String X_BIZ_ERROR_CODE = "x-biz-error-code";
public static final int BIZ_ERROR_STATUS = 4000;
/**
* zookeeper存放接口信息的根目录
*/
public static final String SOP_SERVICE_API_PATH = "/sop-service-api";
}

@ -0,0 +1,9 @@
package com.gitee.sop.gatewaycommon.gateway;
import com.gitee.sop.gatewaycommon.bean.ApiContext;
/**
* @author tanghc
*/
public class GatewayContext extends ApiContext {
}

@ -1,4 +1,4 @@
package com.gitee.sop.gatewaycommon.configuration;
package com.gitee.sop.gatewaycommon.gateway.configuration;
import com.gitee.sop.gatewaycommon.bean.ApiContext;
import com.gitee.sop.gatewaycommon.validate.alipay.AlipaySigner;

@ -1,15 +1,15 @@
package com.gitee.sop.gatewaycommon.configuration;
package com.gitee.sop.gatewaycommon.gateway.configuration;
import com.gitee.sop.gatewaycommon.bean.ApiContext;
import com.gitee.sop.gatewaycommon.handler.GatewayExceptionHandler;
import com.gitee.sop.gatewaycommon.filter.GatewayModifyResponseGatewayFilter;
import com.gitee.sop.gatewaycommon.filter.LoadBalancerClientExtFilter;
import com.gitee.sop.gatewaycommon.filter.ValidateFilter;
import com.gitee.sop.gatewaycommon.manager.GatewayZookeeperApiMetaManager;
import com.gitee.sop.gatewaycommon.gateway.filter.GatewayModifyResponseGatewayFilter;
import com.gitee.sop.gatewaycommon.gateway.filter.LoadBalancerClientExtFilter;
import com.gitee.sop.gatewaycommon.gateway.filter.ValidateFilter;
import com.gitee.sop.gatewaycommon.gateway.handler.GatewayExceptionHandler;
import com.gitee.sop.gatewaycommon.gateway.route.GatewayRouteRepository;
import com.gitee.sop.gatewaycommon.gateway.route.NameVersionRoutePredicateFactory;
import com.gitee.sop.gatewaycommon.gateway.route.ReadBodyRoutePredicateFactory;
import com.gitee.sop.gatewaycommon.gateway.route.GatewayZookeeperRouteManager;
import com.gitee.sop.gatewaycommon.message.ErrorFactory;
import com.gitee.sop.gatewaycommon.route.DynamicRouteServiceManager;
import com.gitee.sop.gatewaycommon.route.NameVersionRoutePredicateFactory;
import com.gitee.sop.gatewaycommon.route.ReadBodyRoutePredicateFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
@ -25,14 +25,6 @@ import javax.annotation.PostConstruct;
import java.util.Collections;
import java.util.List;
//import com.gitee.sop.gatewaycommon.filter.ErrorFilter;
//import com.gitee.sop.gatewaycommon.filter.PostResultFilter;
//import com.gitee.sop.gatewaycommon.filter.PreValidateFilter;
//import com.gitee.sop.gatewaycommon.manager.DefaultApiMetaContext;
//import com.gitee.sop.gatewaycommon.manager.SopRouteLocator;
//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;
/**
* @author tanghc
@ -43,7 +35,7 @@ public class BaseGatewayConfiguration {
protected Environment environment;
@Autowired
protected GatewayZookeeperApiMetaManager gatewayZookeeperApiMetaManager;
protected GatewayZookeeperRouteManager gatewayZookeeperApiMetaManager;
/**
* 自定义异常处理[@@]注册Bean时依赖的Bean会从容器中直接获取所以直接注入即可
@ -65,11 +57,19 @@ public class BaseGatewayConfiguration {
return jsonExceptionHandler;
}
/**
* 处理返回结果
* @return
*/
@Bean
GatewayModifyResponseGatewayFilter gatewayModifyResponseGatewayFilter() {
return new GatewayModifyResponseGatewayFilter();
}
/**
* 读取post请求参数
* @return
*/
@Bean
ReadBodyRoutePredicateFactory readBodyRoutePredicateFactory() {
return new ReadBodyRoutePredicateFactory();
@ -91,13 +91,13 @@ public class BaseGatewayConfiguration {
}
@Bean
GatewayZookeeperApiMetaManager zookeeperApiMetaManager(Environment environment, DynamicRouteServiceManager dynamicRouteServiceManager) {
return new GatewayZookeeperApiMetaManager(environment, dynamicRouteServiceManager);
GatewayZookeeperRouteManager gatewayZookeeperRouteManager(Environment environment, GatewayRouteRepository gatewayRouteManager) {
return new GatewayZookeeperRouteManager(environment, gatewayRouteManager);
}
@Bean
DynamicRouteServiceManager dynamicRouteServiceManager() {
return new DynamicRouteServiceManager();
GatewayRouteRepository gatewayRouteRepository() {
return new GatewayRouteRepository();
}

@ -1,4 +1,4 @@
package com.gitee.sop.gatewaycommon.configuration;
package com.gitee.sop.gatewaycommon.gateway.configuration;
import com.gitee.sop.gatewaycommon.bean.ApiContext;
import com.gitee.sop.gatewaycommon.param.ParamNames;

@ -1,13 +1,11 @@
package com.gitee.sop.gatewaycommon.filter;
package com.gitee.sop.gatewaycommon.gateway.filter;
import com.gitee.sop.gatewaycommon.bean.ApiContext;
import com.gitee.sop.gatewaycommon.result.ResultExecutor;
import org.reactivestreams.Publisher;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.NettyWriteResponseFilter;
import org.springframework.cloud.gateway.filter.factory.rewrite.ModifyResponseBodyGatewayFilterFactory;
import org.springframework.cloud.gateway.support.BodyInserterContext;
import org.springframework.cloud.gateway.support.CachedBodyOutputMessage;
import org.springframework.cloud.gateway.support.DefaultClientResponse;
@ -55,7 +53,7 @@ public class GatewayModifyResponseGatewayFilter implements GlobalFilter, Ordered
Mono modifiedBody = clientResponse.bodyToMono(inClass)
.flatMap(originalBody -> {
// 合并微服务传递过来的结果,变成最终结果
ResultExecutor resultExecutor = ApiContext.getApiConfig().getResultExecutor();
ResultExecutor resultExecutor = ApiContext.getApiConfig().getGatewayResultExecutor();
String ret = resultExecutor.mergeResult(exchange, String.valueOf(originalBody));
return Mono.just(ret);
});

@ -1,4 +1,4 @@
package com.gitee.sop.gatewaycommon.filter;
package com.gitee.sop.gatewaycommon.gateway.filter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;

@ -1,4 +1,4 @@
package com.gitee.sop.gatewaycommon.filter;
package com.gitee.sop.gatewaycommon.gateway.filter;
import com.gitee.sop.gatewaycommon.bean.ApiConfig;
import com.gitee.sop.gatewaycommon.bean.ApiContext;
@ -23,7 +23,7 @@ public class ValidateFilter implements GlobalFilter, Ordered {
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ApiConfig apiConfig = ApiContext.getApiConfig();
// 解析参数
ApiParam param = apiConfig.getParamBuilder().build(exchange);
ApiParam param = apiConfig.getGatewayParamBuilder().build(exchange);
exchange.getAttributes().put(SopConstants.CACHE_API_PARAM, param);
// 验证操作,这里有负责验证签名参数
Validator validator = apiConfig.getValidator();

@ -1,7 +1,7 @@
package com.gitee.sop.gatewaycommon.handler;
package com.gitee.sop.gatewaycommon.gateway.handler;
import com.gitee.sop.gatewaycommon.bean.ApiContext;
import com.gitee.sop.gatewaycommon.result.GatewayResult;
import com.gitee.sop.gatewaycommon.gateway.result.GatewayResult;
import com.gitee.sop.gatewaycommon.result.ResultExecutor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -23,9 +23,8 @@ import java.util.Collections;
import java.util.List;
/**
* @classDesc: 统一异常处理, 参考{@link org.springframework.web.server.AbstractErrorWebExceptionHandler}修改
* @author: chenggang
* @createTime: 2018/10/30
* 统一异常处理
* @author thc
*/
public class GatewayExceptionHandler implements ErrorWebExceptionHandler {
@ -33,7 +32,7 @@ public class GatewayExceptionHandler implements ErrorWebExceptionHandler {
@Override
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
ResultExecutor resultExecutor = ApiContext.getApiConfig().getResultExecutor();
ResultExecutor<ServerWebExchange, GatewayResult> resultExecutor = ApiContext.getApiConfig().getGatewayResultExecutor();
GatewayResult errorResult = resultExecutor.buildErrorResult(exchange, ex);
/**
@ -106,7 +105,6 @@ public class GatewayExceptionHandler implements ErrorWebExceptionHandler {
}
/**
* 参考DefaultErrorWebExceptionHandler
*

@ -1,6 +1,8 @@
package com.gitee.sop.gatewaycommon.param;
package com.gitee.sop.gatewaycommon.gateway.param;
import com.gitee.sop.gatewaycommon.bean.SopConstants;
import com.gitee.sop.gatewaycommon.param.ApiParam;
import com.gitee.sop.gatewaycommon.param.ParamBuilder;
import org.springframework.web.server.ServerWebExchange;
import java.util.Map;
@ -8,7 +10,7 @@ import java.util.Map;
/**
* @author tanghc
*/
public class ApiParamBuilder implements ParamBuilder {
public class GatewayParamBuilder implements ParamBuilder<ServerWebExchange> {
@Override
public ApiParam build(ServerWebExchange exchange) {
Map<String, String> params = exchange.getAttribute(SopConstants.CACHE_REQUEST_BODY_FOR_MAP);

@ -1,4 +1,4 @@
package com.gitee.sop.gatewaycommon.result;
package com.gitee.sop.gatewaycommon.gateway.result;
import lombok.Data;
import org.springframework.http.HttpStatus;

@ -0,0 +1,70 @@
package com.gitee.sop.gatewaycommon.gateway.result;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
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.result.BaseExecutorAdapter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.util.CollectionUtils;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.server.ServerWebExchange;
import java.util.List;
import java.util.Map;
/**
* @author tanghc
*/
@Slf4j
public class GatewayResultExecutor extends BaseExecutorAdapter<ServerWebExchange, GatewayResult> {
@Override
public int getBizHeaderCode(ServerWebExchange exchange) {
int responseStatus = HttpStatus.OK.value();
List<String> errorCodeList = exchange.getResponse().getHeaders().get(SopConstants.X_BIZ_ERROR_CODE);
if (!CollectionUtils.isEmpty(errorCodeList)) {
String errorCode = errorCodeList.get(0);
responseStatus = Integer.valueOf(errorCode);
}
return responseStatus;
}
@Override
public Map<String, ?> getApiParam(ServerWebExchange exchange) {
return exchange.getAttribute(SopConstants.CACHE_REQUEST_BODY_FOR_MAP);
}
@Override
public GatewayResult buildErrorResult(ServerWebExchange exchange, Throwable ex) {
Error error = null;
if (ex instanceof ApiException) {
ApiException apiException = (ApiException) ex;
error = apiException.getError();
} else if (ex instanceof NotFoundException) {
error = ErrorEnum.ISV_INVALID_METHOD.getErrorMeta().getError();
} else if (ex instanceof ResponseStatusException) {
ResponseStatusException responseStatusException = (ResponseStatusException) ex;
HttpStatus status = responseStatusException.getStatus();
if (status == HttpStatus.NOT_FOUND) {
error = ErrorEnum.ISV_INVALID_METHOD.getErrorMeta().getError();
}
}
if (error == null) {
error = ErrorEnum.AOP_UNKNOW_ERROR.getErrorMeta().getError();
}
JSONObject jsonObject = (JSONObject) JSON.toJSON(error);
String body = this.merge(exchange, jsonObject);
return new GatewayResult(HttpStatus.OK, MediaType.APPLICATION_JSON_UTF8, body);
}
}

@ -1,4 +1,4 @@
package com.gitee.sop.gatewaycommon.route;
package com.gitee.sop.gatewaycommon.gateway.route;
import lombok.Getter;
import lombok.Setter;

@ -1,4 +1,4 @@
package com.gitee.sop.gatewaycommon.route;
package com.gitee.sop.gatewaycommon.gateway.route;
import lombok.Data;
@ -10,8 +10,8 @@ import java.util.Map;
*/
@Data
public class GatewayFilterDefinition {
//Filter Name */
/** Filter Name */
private String name;
//对应的路由规则 */
/** 对应的路由规则 */
private Map<String, String> args = new LinkedHashMap<>();
}

@ -1,6 +1,9 @@
package com.gitee.sop.gatewaycommon.route;
package com.gitee.sop.gatewaycommon.gateway.route;
import com.gitee.sop.gatewaycommon.bean.BaseRouteDefinition;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import java.util.ArrayList;
import java.util.List;
@ -8,16 +11,11 @@ import java.util.List;
/**
* @author tanghc
*/
@Data
public class GatewayRouteDefinition {
/** 路由的Id */
private String id;
@Getter
@Setter
public class GatewayRouteDefinition extends BaseRouteDefinition {
/** 路由断言集合配置 */
private List<GatewayPredicateDefinition> predicates = new ArrayList<>();
/** 路由过滤器集合配置 */
private List<GatewayFilterDefinition> filters = new ArrayList<>();
/** 路由规则转发的目标uri */
private String uri;
/** 路由执行的顺序 */
private int order = 0;
}

@ -1,5 +1,6 @@
package com.gitee.sop.gatewaycommon.route;
package com.gitee.sop.gatewaycommon.gateway.route;
import com.gitee.sop.gatewaycommon.manager.RouteRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
@ -16,7 +17,7 @@ import reactor.core.publisher.Mono;
* @author thc
*/
@Slf4j
public class DynamicRouteServiceManager implements ApplicationEventPublisherAware {
public class GatewayRouteRepository implements ApplicationEventPublisherAware, RouteRepository<RouteDefinition> {
@Autowired
private RouteDefinitionRepository routeDefinitionRepository;
@ -24,6 +25,7 @@ public class DynamicRouteServiceManager implements ApplicationEventPublisherAwar
private ApplicationEventPublisher publisher;
/** 根据ID获取路由 */
@Override
public RouteDefinition get(String id) {
return routeDefinitionRepository.getRouteDefinitions()
.filter(routeDefinition -> {
@ -32,6 +34,7 @@ public class DynamicRouteServiceManager implements ApplicationEventPublisherAwar
}
/** 增加路由 */
@Override
public String add(RouteDefinition definition) {
routeDefinitionRepository.save(Mono.just(definition)).subscribe();
this.publisher.publishEvent(new RefreshRoutesEvent(this));
@ -39,7 +42,9 @@ public class DynamicRouteServiceManager implements ApplicationEventPublisherAwar
}
/** 更新路由 */
@Override
public String update(RouteDefinition definition) {
log.info("更新route,id:{}", definition.getId());
try {
this.routeDefinitionRepository.delete(Mono.just(definition.getId()));
} catch (Exception e) {
@ -55,8 +60,9 @@ public class DynamicRouteServiceManager implements ApplicationEventPublisherAwar
}
/** 删除路由 */
public Mono<ResponseEntity<Object>> delete(String id) {
return this.routeDefinitionRepository.delete(Mono.just(id))
@Override
public void delete(String id) {
this.routeDefinitionRepository.delete(Mono.just(id))
.then(Mono.defer(() -> Mono.just(ResponseEntity.ok().build())))
.onErrorResume(t -> t instanceof NotFoundException, t -> Mono.just(ResponseEntity.notFound().build()));
}

@ -0,0 +1,12 @@
package com.gitee.sop.gatewaycommon.gateway.route;
import com.gitee.sop.gatewaycommon.bean.BaseServiceRouteInfo;
import lombok.Data;
import java.util.List;
/**
* @author thc
*/
public class GatewayServiceRouteInfo extends BaseServiceRouteInfo<GatewayRouteDefinition> {
}

@ -0,0 +1,65 @@
package com.gitee.sop.gatewaycommon.gateway.route;
import com.gitee.sop.gatewaycommon.manager.BaseRouteManager;
import com.gitee.sop.gatewaycommon.manager.RouteRepository;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.core.env.Environment;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
/**
* 从zookeeper监听route信息
*
* @author tanghc
*/
@Getter
@Slf4j
public class GatewayZookeeperRouteManager extends BaseRouteManager<GatewayRouteDefinition, GatewayServiceRouteInfo, RouteDefinition> {
public GatewayZookeeperRouteManager(Environment environment, RouteRepository<RouteDefinition> routeRepository) {
super(environment, routeRepository);
}
@Override
protected Class<GatewayServiceRouteInfo> getServiceRouteInfoClass() {
return GatewayServiceRouteInfo.class;
}
@Override
protected Class<GatewayRouteDefinition> getRouteDefinitionClass() {
return GatewayRouteDefinition.class;
}
@Override
protected RouteDefinition buildRouteDefinition(GatewayServiceRouteInfo serviceRouteInfo,GatewayRouteDefinition gatewayRouteDefinition) {
RouteDefinition routeDefinition = new RouteDefinition();
routeDefinition.setId(gatewayRouteDefinition.getId());
routeDefinition.setUri(URI.create(gatewayRouteDefinition.getUri()));
routeDefinition.setOrder(gatewayRouteDefinition.getOrder());
List<FilterDefinition> filterDefinitionList = new ArrayList<>(gatewayRouteDefinition.getFilters().size());
List<PredicateDefinition> predicateDefinitionList = new ArrayList<>(gatewayRouteDefinition.getPredicates().size());
for (GatewayFilterDefinition filter : gatewayRouteDefinition.getFilters()) {
FilterDefinition filterDefinition = new FilterDefinition();
BeanUtils.copyProperties(filter, filterDefinition);
filterDefinitionList.add(filterDefinition);
}
for (GatewayPredicateDefinition predicate : gatewayRouteDefinition.getPredicates()) {
PredicateDefinition predicateDefinition = new PredicateDefinition();
BeanUtils.copyProperties(predicate, predicateDefinition);
predicateDefinitionList.add(predicateDefinition);
}
routeDefinition.setFilters(filterDefinitionList);
routeDefinition.setPredicates(predicateDefinitionList);
return routeDefinition;
}
}

@ -1,4 +1,4 @@
package com.gitee.sop.gatewaycommon.route;
package com.gitee.sop.gatewaycommon.gateway.route;
import com.gitee.sop.gatewaycommon.bean.SopConstants;
import com.gitee.sop.gatewaycommon.param.ParamNames;

@ -1,4 +1,4 @@
package com.gitee.sop.gatewaycommon.route;
package com.gitee.sop.gatewaycommon.gateway.route;
import org.springframework.cloud.gateway.handler.predicate.ReadBodyPredicateFactory;

@ -1,20 +0,0 @@
package com.gitee.sop.gatewaycommon.manager;
import org.springframework.data.redis.core.StringRedisTemplate;
/**
* @author tanghc
*/
public class ApiMetaConfig {
private StringRedisTemplate redisTemplate;
public ApiMetaConfig(StringRedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
public void loadApiMetas() {
}
}

@ -1,22 +0,0 @@
package com.gitee.sop.gatewaycommon.manager;
/**
* 管理各服务接口信息
* @author tanghc
*/
public interface ApiMetaManager {
String API_STORE_KEY = "com.gitee.sop.api";
/**
* 刷新素有的微服务接口信息
*/
void refresh();
/**
* 某个服务接口更改时触发
* @param serviceApiInfoJson 接口信息
*/
void onChange(String serviceApiInfoJson);
}

@ -0,0 +1,135 @@
package com.gitee.sop.gatewaycommon.manager;
import com.alibaba.fastjson.JSON;
import com.gitee.sop.gatewaycommon.bean.BaseRouteDefinition;
import com.gitee.sop.gatewaycommon.bean.BaseServiceRouteInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.cache.ChildData;
import org.apache.curator.framework.recipes.cache.PathChildrenCache;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;
import java.util.List;
import static com.gitee.sop.gatewaycommon.bean.SopConstants.SOP_SERVICE_API_PATH;
/**
* @author tanghc
*/
@Slf4j
public abstract class BaseRouteManager<E extends BaseRouteDefinition,R extends BaseServiceRouteInfo<E>, T> implements RouteManager {
protected String sopServiceApiPath = SOP_SERVICE_API_PATH;
protected Environment environment;
protected RouteRepository<T> routeRepository;
protected abstract Class<R> getServiceRouteInfoClass();
protected abstract Class<E> getRouteDefinitionClass();
protected abstract T buildRouteDefinition(R serviceRouteInfo, E routeDefinition);
public BaseRouteManager(Environment environment, RouteRepository<T> routeRepository) {
this.environment = environment;
this.routeRepository = routeRepository;
}
@Override
public void refresh() {
log.info("刷新本地接口信息");
try {
String zookeeperServerAddr = environment.getProperty("spring.cloud.zookeeper.connect-string");
if (StringUtils.isEmpty(zookeeperServerAddr)) {
throw new RuntimeException("未指定spring.cloud.zookeeper.connect-string参数");
}
CuratorFramework client = CuratorFrameworkFactory.builder()
.connectString(zookeeperServerAddr)
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
.build();
client.start();
client.create()
// 如果节点存在则Curator将会使用给出的数据设置这个节点的值
.orSetData()
// 如果指定节点的父节点不存在,则Curator将会自动级联创建父节点
.creatingParentContainersIfNeeded()
.forPath(sopServiceApiPath, "".getBytes());
this.watchChildren(client, sopServiceApiPath);
} catch (Exception e) {
e.printStackTrace();
}
}
protected void watchChildren(CuratorFramework client, String sopServiceApiPath) throws Exception {
// 为子节点添加watcher
// PathChildrenCache: 监听数据节点的增删改,可以设置触发的事件
final PathChildrenCache childrenCache = new PathChildrenCache(client, sopServiceApiPath, true);
/**
* StartMode: 初始化方式
* POST_INITIALIZED_EVENT异步初始化初始化之后会触发事件
* NORMAL异步初始化
* BUILD_INITIAL_CACHE同步初始化
*/
childrenCache.start(PathChildrenCache.StartMode.BUILD_INITIAL_CACHE);
// 列出子节点数据列表,需要使用BUILD_INITIAL_CACHE同步初始化模式才能获得,异步是获取不到的
List<ChildData> childDataList = childrenCache.getCurrentData();
log.info("微服务API详细数据列表:");
for (ChildData childData : childDataList) {
String nodeData = new String(childData.getData());
log.info("\t* 子节点路径:" + childData.getPath() + ",该节点的数据为:" + nodeData);
R serviceRouteInfo = JSON.parseObject(nodeData, getServiceRouteInfoClass());
for (E routeDefinitionItem : serviceRouteInfo.getRouteDefinitionList()) {
T routeDefinition = buildRouteDefinition(serviceRouteInfo, routeDefinitionItem);
routeRepository.add(routeDefinition);
}
}
// 添加事件监听器
childrenCache.getListenable().addListener(new PathChildrenCacheListener() {
@Override
public void childEvent(CuratorFramework curatorFramework, PathChildrenCacheEvent event) throws Exception {
PathChildrenCacheEvent.Type type = event.getType();
// 通过判断event type的方式来实现不同事件的触发
if (PathChildrenCacheEvent.Type.CHILD_ADDED.equals(type)) {
String nodeData = new String(event.getData().getData());
R serviceRouteInfo = JSON.parseObject(nodeData, getServiceRouteInfoClass());
// 添加子节点时触发
log.info("子节点:{}添加,数据为:{}", event.getData().getPath(), nodeData);
for (E routeDefinitionItem : serviceRouteInfo.getRouteDefinitionList()) {
T routeDefinition = buildRouteDefinition(serviceRouteInfo, routeDefinitionItem);
routeRepository.add(routeDefinition);
}
} else if (PathChildrenCacheEvent.Type.CHILD_UPDATED.equals(type)) {
String nodeData = new String(event.getData().getData());
R serviceRouteInfo = JSON.parseObject(nodeData, getServiceRouteInfoClass());
// 修改子节点数据时触发
log.info("子节点:{}修改,数据为:{}", event.getData().getPath(), nodeData);
for (E routeDefinitionItem : serviceRouteInfo.getRouteDefinitionList()) {
T routeDefinition = buildRouteDefinition(serviceRouteInfo, routeDefinitionItem);
routeRepository.update(routeDefinition);
}
} else if (PathChildrenCacheEvent.Type.CHILD_REMOVED.equals(type)) {
String nodeData = new String(event.getData().getData());
R serviceRouteInfo = JSON.parseObject(nodeData, getServiceRouteInfoClass());
// 删除子节点时触发
log.info("子节点:{}删除,数据为:{}", event.getData().getPath(), nodeData);
for (E routeDefinitionItem : serviceRouteInfo.getRouteDefinitionList()) {
routeRepository.delete(routeDefinitionItem.getId());
}
}
}
});
}
}

@ -1,168 +0,0 @@
package com.gitee.sop.gatewaycommon.manager;
import com.alibaba.fastjson.JSON;
import com.gitee.sop.gatewaycommon.route.DynamicRouteServiceManager;
import com.gitee.sop.gatewaycommon.route.GatewayFilterDefinition;
import com.gitee.sop.gatewaycommon.route.GatewayPredicateDefinition;
import com.gitee.sop.gatewaycommon.route.GatewayRouteDefinition;
import com.gitee.sop.gatewaycommon.route.ServiceRouteInfo;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.cache.ChildData;
import org.apache.curator.framework.recipes.cache.PathChildrenCache;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.springframework.beans.BeanUtils;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
/**
* 从zookeeper监听route信息
*
* @author tanghc
*/
@Getter
@Slf4j
public class GatewayZookeeperApiMetaManager implements ApiMetaManager {
private String sopServiceApiPath = "/sop-service-api";
private Environment environment;
private DynamicRouteServiceManager dynamicRouteServiceManager;
public GatewayZookeeperApiMetaManager(Environment environment, DynamicRouteServiceManager dynamicRouteServiceManager) {
this.environment = environment;
this.dynamicRouteServiceManager = dynamicRouteServiceManager;
}
@Override
public void refresh() {
log.info("刷新本地接口信息");
try {
String zookeeperServerAddr = environment.getProperty("spring.cloud.zookeeper.connect-string");
if (StringUtils.isEmpty(zookeeperServerAddr)) {
throw new RuntimeException("未指定spring.cloud.zookeeper.connect-string参数");
}
CuratorFramework client = CuratorFrameworkFactory.builder()
.connectString(zookeeperServerAddr)
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
.build();
client.start();
client.create()
// 如果节点存在则Curator将会使用给出的数据设置这个节点的值
.orSetData()
// 如果指定节点的父节点不存在,则Curator将会自动级联创建父节点
.creatingParentContainersIfNeeded()
.forPath(sopServiceApiPath, "".getBytes());
this.watchChildren(client, sopServiceApiPath);
} catch (Exception e) {
e.printStackTrace();
}
}
protected void watchChildren(CuratorFramework client, String sopServiceApiPath) throws Exception {
// 为子节点添加watcher
// PathChildrenCache: 监听数据节点的增删改,可以设置触发的事件
final PathChildrenCache childrenCache = new PathChildrenCache(client, sopServiceApiPath, true);
/**
* StartMode: 初始化方式
* POST_INITIALIZED_EVENT异步初始化初始化之后会触发事件
* NORMAL异步初始化
* BUILD_INITIAL_CACHE同步初始化
*/
childrenCache.start(PathChildrenCache.StartMode.BUILD_INITIAL_CACHE);
// 列出子节点数据列表,需要使用BUILD_INITIAL_CACHE同步初始化模式才能获得,异步是获取不到的
List<ChildData> childDataList = childrenCache.getCurrentData();
log.info("微服务API详细数据列表:");
for (ChildData childData : childDataList) {
String nodeData = new String(childData.getData());
log.info("\t* 子节点路径:" + childData.getPath() + ",该节点的数据为:" + nodeData);
ServiceRouteInfo serviceRouteInfo = JSON.parseObject(nodeData, ServiceRouteInfo.class);
for (GatewayRouteDefinition gatewayRouteDefinition : serviceRouteInfo.getRouteDefinitionList()) {
RouteDefinition routeDefinition = buildRouteDefinition(gatewayRouteDefinition);
dynamicRouteServiceManager.add(routeDefinition);
}
}
// 添加事件监听器
childrenCache.getListenable().addListener(new PathChildrenCacheListener() {
@Override
public void childEvent(CuratorFramework curatorFramework, PathChildrenCacheEvent event) throws Exception {
PathChildrenCacheEvent.Type type = event.getType();
// 通过判断event type的方式来实现不同事件的触发
if (PathChildrenCacheEvent.Type.CHILD_ADDED.equals(type)) {
String nodeData = new String(event.getData().getData());
ServiceRouteInfo serviceRouteInfo = JSON.parseObject(nodeData, ServiceRouteInfo.class);
// 添加子节点时触发
log.info("子节点:{}添加成功,数据为:{}", event.getData().getPath(), nodeData);
for (GatewayRouteDefinition gatewayRouteDefinition : serviceRouteInfo.getRouteDefinitionList()) {
RouteDefinition routeDefinition = buildRouteDefinition(gatewayRouteDefinition);
dynamicRouteServiceManager.add(routeDefinition);
}
} else if (PathChildrenCacheEvent.Type.CHILD_UPDATED.equals(type)) {
String nodeData = new String(event.getData().getData());
ServiceRouteInfo serviceRouteInfo = JSON.parseObject(nodeData, ServiceRouteInfo.class);
// 修改子节点数据时触发
log.info("子节点:{}修改成功,数据为:{}", event.getData().getPath(), nodeData);
for (GatewayRouteDefinition gatewayRouteDefinition : serviceRouteInfo.getRouteDefinitionList()) {
RouteDefinition routeDefinition = buildRouteDefinition(gatewayRouteDefinition);
dynamicRouteServiceManager.update(routeDefinition);
}
} else if (PathChildrenCacheEvent.Type.CHILD_REMOVED.equals(type)) {
String nodeData = new String(event.getData().getData());
ServiceRouteInfo serviceRouteInfo = JSON.parseObject(nodeData, ServiceRouteInfo.class);
// 删除子节点时触发
log.info("子节点:{}删除成功,数据为:{}", event.getData().getPath(), nodeData);
for (GatewayRouteDefinition gatewayRouteDefinition : serviceRouteInfo.getRouteDefinitionList()) {
dynamicRouteServiceManager.delete(gatewayRouteDefinition.getId());
}
}
}
});
}
protected RouteDefinition buildRouteDefinition(GatewayRouteDefinition gatewayRouteDefinition) {
RouteDefinition routeDefinition = new RouteDefinition();
routeDefinition.setId(gatewayRouteDefinition.getId());
routeDefinition.setUri(URI.create(gatewayRouteDefinition.getUri()));
routeDefinition.setOrder(gatewayRouteDefinition.getOrder());
List<FilterDefinition> filterDefinitionList = new ArrayList<>(gatewayRouteDefinition.getFilters().size());
List<PredicateDefinition> predicateDefinitionList = new ArrayList<>(gatewayRouteDefinition.getPredicates().size());
for (GatewayFilterDefinition filter : gatewayRouteDefinition.getFilters()) {
FilterDefinition filterDefinition = new FilterDefinition();
BeanUtils.copyProperties(filter, filterDefinition);
filterDefinitionList.add(filterDefinition);
}
for (GatewayPredicateDefinition predicate : gatewayRouteDefinition.getPredicates()) {
PredicateDefinition predicateDefinition = new PredicateDefinition();
BeanUtils.copyProperties(predicate, predicateDefinition);
predicateDefinitionList.add(predicateDefinition);
}
routeDefinition.setFilters(filterDefinitionList);
routeDefinition.setPredicates(predicateDefinitionList);
return routeDefinition;
}
@Override
public void onChange(String serviceApiInfoJson) {
}
}

@ -0,0 +1,16 @@
package com.gitee.sop.gatewaycommon.manager;
/**
* 管理各服务路由信息
* @author tanghc
*/
public interface RouteManager {
String API_STORE_KEY = "com.gitee.sop.api";
/**
* 刷新素有的微服务接口信息
*/
void refresh();
}

@ -0,0 +1,14 @@
package com.gitee.sop.gatewaycommon.manager;
/**
* @author tanghc
*/
public interface RouteRepository<T> {
T get(String id);
String add(T route);
String update(T route);
void delete(String id);
}

@ -1,16 +1,14 @@
package com.gitee.sop.gatewaycommon.param;
import org.springframework.web.server.ServerWebExchange;
/**
* @author tanghc
*/
public interface ParamBuilder {
public interface ParamBuilder<T> {
/**
* 从request提取参数
* @param exchange
* @param request
* @return 返回ApiParam
* @throws Exception
*/
ApiParam build(ServerWebExchange exchange);
ApiParam build(T request);
}

@ -1,146 +0,0 @@
package com.gitee.sop.gatewaycommon.result;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
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.message.ErrorMeta;
import com.gitee.sop.gatewaycommon.param.ParamNames;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.util.CollectionUtils;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.server.ServerWebExchange;
import java.util.List;
import java.util.Map;
/**
* @author tanghc
*/
@Slf4j
public class ApiResultExecutor implements ResultExecutor {
private static final ErrorMeta SUCCESS_META = ErrorEnum.SUCCESS.getErrorMeta();
private static final ErrorMeta ISP_UNKNOW_ERROR_META = ErrorEnum.ISP_UNKNOW_ERROR.getErrorMeta();
private static final ErrorMeta ISP_BIZ_ERROR = ErrorEnum.BIZ_ERROR.getErrorMeta();
public static final int BIZ_ERROR_STATUS = 4000;
private static final char DOT = '.';
private static final char UNDERLINE = '_';
public static final String GATEWAY_CODE_NAME = "code";
public static final String GATEWAY_MSG_NAME = "msg";
public static final String DATA_SUFFIX = "_response";
@Override
public String mergeResult(ServerWebExchange exchange, String responseData) {
int responseStatus = HttpStatus.OK.value();
List<String> errorCodeList = exchange.getResponse().getHeaders().get(SopConstants.X_BIZ_ERROR_CODE);
if (!CollectionUtils.isEmpty(errorCodeList)) {
String errorCode = errorCodeList.get(0);
responseStatus = Integer.valueOf(errorCode);
}
if (responseStatus == HttpStatus.OK.value() || responseStatus == BIZ_ERROR_STATUS) {
return mergeSuccess(exchange, responseStatus, responseData);
} else {
// 微服务端有可能返回500错误
// {"path":"/book/getBook3","error":"Internal Server Error","message":"id不能为空","timestamp":"2019-02-13T07:41:00.495+0000","status":500}
return mergeError(exchange, responseData);
}
}
@Override
public GatewayResult buildErrorResult(ServerWebExchange exchange, Throwable ex) {
Error error = null;
if (ex instanceof ApiException) {
ApiException apiException = (ApiException) ex;
error = apiException.getError();
} else if (ex instanceof NotFoundException) {
error = ErrorEnum.ISV_INVALID_METHOD.getErrorMeta().getError();
} else if (ex instanceof ResponseStatusException) {
ResponseStatusException responseStatusException = (ResponseStatusException) ex;
HttpStatus status = responseStatusException.getStatus();
if (status == HttpStatus.NOT_FOUND) {
error = ErrorEnum.ISV_INVALID_METHOD.getErrorMeta().getError();
}
}
if (error == null) {
error = ErrorEnum.AOP_UNKNOW_ERROR.getErrorMeta().getError();
}
JSONObject jsonObject = (JSONObject) JSON.toJSON(error);
String body = this.merge(exchange, jsonObject);
return new GatewayResult(HttpStatus.OK, MediaType.APPLICATION_JSON_UTF8, body);
}
/*
成功示例
{
"alipay_trade_fastpay_refund_query_response": {
"code": "10000",
"msg": "Success",
"trade_no": "2014112611001004680073956707",
"out_trade_no": "20150320010101001",
"out_request_no": "20150320010101001",
"refund_reason": "用户退款请求",
"total_amount": 100.2,
"refund_amount": 12.33
},
"sign": "ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE"
}
*/
public String mergeSuccess(ServerWebExchange exchange, int responseStatus, String serviceResult) {
JSONObject jsonObjectService;
// 如果是业务出错
if (responseStatus == BIZ_ERROR_STATUS) {
jsonObjectService = JSON.parseObject(serviceResult);
jsonObjectService.put(GATEWAY_CODE_NAME, ISP_BIZ_ERROR.getCode());
jsonObjectService.put(GATEWAY_MSG_NAME, ISP_BIZ_ERROR.getError().getMsg());
} else {
// 200正常返回
jsonObjectService = JSON.parseObject(serviceResult);
jsonObjectService.put(GATEWAY_CODE_NAME, SUCCESS_META.getCode());
jsonObjectService.put(GATEWAY_MSG_NAME, SUCCESS_META.getError().getMsg());
}
return this.merge(exchange, jsonObjectService);
}
/*
异常示例
{
"alipay_trade_fastpay_refund_query_response": {
"code": "20000",
"msg": "Service Currently Unavailable",
"sub_code": "isp.unknow-error",
"sub_msg": "系统繁忙"
},
"sign": "ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE"
}
*/
public String mergeError(ServerWebExchange exchange, String serviceResult) {
JSONObject jsonObjectService = new JSONObject();
jsonObjectService.put(GATEWAY_CODE_NAME, ISP_UNKNOW_ERROR_META.getCode());
jsonObjectService.put(GATEWAY_MSG_NAME, ISP_UNKNOW_ERROR_META.getError().getMsg());
return this.merge(exchange, jsonObjectService);
}
private String merge(ServerWebExchange exchange, JSONObject jsonObjectService) {
JSONObject ret = new JSONObject();
// 点换成下划线
Map<String, String> params = exchange.getAttribute(SopConstants.CACHE_REQUEST_BODY_FOR_MAP);
String apiName = params.getOrDefault(ParamNames.API_NAME, "error").replace(DOT, UNDERLINE);
ret.put(apiName + DATA_SUFFIX, jsonObjectService);
ret.put(ParamNames.SIGN_NAME, params.getOrDefault(ParamNames.SIGN_NAME, ""));
return ret.toJSONString();
}
}

@ -0,0 +1,82 @@
package com.gitee.sop.gatewaycommon.result;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.gitee.sop.gatewaycommon.bean.SopConstants;
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
import com.gitee.sop.gatewaycommon.message.ErrorMeta;
import com.gitee.sop.gatewaycommon.param.ParamNames;
import org.springframework.http.HttpStatus;
import java.util.Map;
/**
* @author tanghc
*/
public abstract class BaseExecutorAdapter<T, R> implements ResultExecutor<T, R> {
private static final ErrorMeta SUCCESS_META = ErrorEnum.SUCCESS.getErrorMeta();
private static final ErrorMeta ISP_UNKNOW_ERROR_META = ErrorEnum.ISP_UNKNOW_ERROR.getErrorMeta();
private static final ErrorMeta ISP_BIZ_ERROR = ErrorEnum.BIZ_ERROR.getErrorMeta();
private static final char DOT = '.';
private static final char UNDERLINE = '_';
public static final String GATEWAY_CODE_NAME = "code";
public static final String GATEWAY_MSG_NAME = "msg";
public static final String DATA_SUFFIX = "_response";
/**
* 获取业务方约定的返回码
* @param t
* @return
*/
public abstract int getBizHeaderCode(T t);
/**
* 返回Api参数
* @param t
* @return
*/
public abstract Map<String, ?> getApiParam(T t);
@Override
public String mergeResult(T request, String serviceResult) {
int responseStatus = this.getBizHeaderCode(request);
JSONObject jsonObjectService;
if (responseStatus == HttpStatus.OK.value()) {
// 200正常返回
jsonObjectService = JSON.parseObject(serviceResult);
jsonObjectService.put(GATEWAY_CODE_NAME, SUCCESS_META.getCode());
jsonObjectService.put(GATEWAY_MSG_NAME, SUCCESS_META.getError().getMsg());
} else if (responseStatus == SopConstants.BIZ_ERROR_STATUS) {
// 如果是业务出错
jsonObjectService = JSON.parseObject(serviceResult);
jsonObjectService.put(GATEWAY_CODE_NAME, ISP_BIZ_ERROR.getCode());
jsonObjectService.put(GATEWAY_MSG_NAME, ISP_BIZ_ERROR.getError().getMsg());
} else {
// 微服务端有可能返回500错误
// {"path":"/book/getBook3","error":"Internal Server Error","message":"id不能为空","timestamp":"2019-02-13T07:41:00.495+0000","status":500}
jsonObjectService = new JSONObject();
jsonObjectService.put(GATEWAY_CODE_NAME, ISP_UNKNOW_ERROR_META.getCode());
jsonObjectService.put(GATEWAY_MSG_NAME, ISP_UNKNOW_ERROR_META.getError().getMsg());
}
return this.merge(request, jsonObjectService);
}
public String merge(T exchange, JSONObject jsonObjectService) {
JSONObject ret = new JSONObject();
// 点换成下划线
Map<String, ?> params = this.getApiParam(exchange);
Object name = params.get(ParamNames.API_NAME);
if (name == null) {
name = "error";
}
Object sign = params.get(ParamNames.SIGN_NAME);
if (sign == null) {
sign = "";
}
String method = String.valueOf(name).replace(DOT, UNDERLINE);
ret.put(method + DATA_SUFFIX, jsonObjectService);
ret.put(ParamNames.SIGN_NAME, sign);
return ret.toJSONString();
}
}

@ -1,25 +1,51 @@
package com.gitee.sop.gatewaycommon.result;
import org.springframework.web.server.ServerWebExchange;
import com.gitee.sop.gatewaycommon.gateway.result.GatewayResult;
/**
* 对返回结果进行处理
* 成功示例
* {
* "alipay_trade_fastpay_refund_query_response": {
* "code": "10000",
* "msg": "Success",
* "trade_no": "2014112611001004680073956707",
* "out_trade_no": "20150320010101001",
* "out_request_no": "20150320010101001",
* "refund_reason": "用户退款请求",
* "total_amount": 100.2,
* "refund_amount": 12.33
* },
* "sign": "ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE"
* }
* <p>
* 异常示例
* {
* "alipay_trade_fastpay_refund_query_response": {
* "code": "20000",
* "msg": "Service Currently Unavailable",
* "sub_code": "isp.unknow-error",
* "sub_msg": "系统繁忙"
* },
* "sign": "ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE"
* }
*
* @author tanghc
*/
public interface ResultExecutor {
public interface ResultExecutor<T, R> {
/**
* 合并结果
* @param exchange
* @param responseData
* @param request
* @param serviceResult
* @return
*/
String mergeResult(ServerWebExchange exchange, String responseData);
String mergeResult(T request, String serviceResult);
/**
* 合并错误结果
* @param exchange
* @param request
* @param ex
* @return
*/
GatewayResult buildErrorResult(ServerWebExchange exchange, Throwable ex);
R buildErrorResult(T request, Throwable ex);
}

@ -1,12 +0,0 @@
package com.gitee.sop.gatewaycommon.route;
import lombok.Data;
import java.util.List;
@Data
public class ServiceRouteInfo {
private String appName;
private String md5;
private List<GatewayRouteDefinition> routeDefinitionList;
}

@ -1,12 +1,15 @@
package com.gitee.sop.gatewaycommon.util;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
@ -43,4 +46,38 @@ public class RequestUtil {
return params;
}
/**
* request中的参数转换成map
*
* @param request request对象
* @return 返回参数键值对
*/
public static Map<String, Object> convertRequestParamsToMap(HttpServletRequest request) {
Map<String, String[]> paramMap = request.getParameterMap();
if(paramMap == null || paramMap.isEmpty()) {
return Collections.emptyMap();
}
Map<String, Object> retMap = new HashMap<String, Object>(paramMap.size());
Set<Map.Entry<String, String[]>> entrySet = paramMap.entrySet();
for (Map.Entry<String, String[]> entry : entrySet) {
String name = entry.getKey();
String[] values = entry.getValue();
if (values.length == 1) {
retMap.put(name, values[0]);
} else if (values.length > 1) {
retMap.put(name, values);
} else {
retMap.put(name, "");
}
}
return retMap;
}
public static String getText(HttpServletRequest request) throws Exception {
return IOUtils.toString(request.getInputStream(), UTF8);
}
}

@ -0,0 +1,167 @@
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.bean.SopConstants;
import com.gitee.sop.gatewaycommon.param.UploadContext;
import com.netflix.zuul.context.RequestContext;
import com.gitee.sop.gatewaycommon.param.ApiParam;
import com.gitee.sop.gatewaycommon.session.SessionManager;
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 final String ATTR_UPLOAD_CONTEXT = "zuul.common.api.upload_context";
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();
}
/**
* 返回自定义的session,被SessionManager管理
*
* @return 如果sessionId为null则返回null
*/
public static HttpSession getManagedSession() {
String sessionId = getSessionId();
if (sessionId != null) {
return getSessionManager().getSession(sessionId);
} else {
return null;
}
}
/**
* 获取登陆的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;
}
/**
* 获取上传文件如果客户端有文件上传从这里取
* @return 如果没有文件上传返回null
*/
public static UploadContext getUploadContext() {
return (UploadContext) getAttr(ATTR_UPLOAD_CONTEXT);
}
public static void setUploadContext(UploadContext uploadCtx) {
setAttr(ATTR_UPLOAD_CONTEXT, uploadCtx);
}
/**
* 获取response
* @return 返回response
*/
public static HttpServletResponse getResponse() {
return RequestContext.getCurrentContext().getResponse();
}
}

@ -0,0 +1,16 @@
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());
}
}

@ -0,0 +1,92 @@
package com.gitee.sop.gatewaycommon.zuul.configuration;
import com.gitee.sop.gatewaycommon.bean.ApiContext;
import com.gitee.sop.gatewaycommon.manager.RouteManager;
import com.gitee.sop.gatewaycommon.message.ErrorFactory;
import com.gitee.sop.gatewaycommon.zuul.filter.ErrorFilter;
import com.gitee.sop.gatewaycommon.zuul.filter.PostResultFilter;
import com.gitee.sop.gatewaycommon.zuul.filter.PreValidateFilter;
import com.gitee.sop.gatewaycommon.zuul.route.SopRouteLocator;
import com.gitee.sop.gatewaycommon.zuul.route.ZuulRouteRepository;
import com.gitee.sop.gatewaycommon.zuul.route.ZuulZookeeperRouteManager;
import org.springframework.beans.factory.annotation.Autowired;
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;
import org.springframework.core.env.Environment;
import javax.annotation.PostConstruct;
/**
* @author tanghc
*/
public class BaseZuulConfiguration {
@Autowired
protected ZuulProperties zuulProperties;
@Autowired
protected ServerProperties server;
@Autowired
protected Environment environment;
@Autowired
protected RouteManager apiMetaManager;
@Bean
ZuulZookeeperRouteManager zuulZookeeperRouteManager(Environment environment, ZuulRouteRepository zuulRouteRepository) {
return new ZuulZookeeperRouteManager(environment, zuulRouteRepository);
}
@Bean
ZuulRouteRepository zuulRouteRepository() {
return new ZuulRouteRepository();
}
@Bean
PreValidateFilter preValidateFilter() {
return new PreValidateFilter();
}
@Bean
public PreDecorationFilter preDecorationFilter(ZuulRouteRepository zuulRouteRepository, ProxyRequestHelper proxyRequestHelper) {
SopRouteLocator routeLocator = new SopRouteLocator(zuulRouteRepository);
return new PreDecorationFilter(routeLocator,
this.server.getServlet().getContextPath(),
this.zuulProperties,
proxyRequestHelper);
}
@Bean
ErrorFilter errorFilter() {
return new ErrorFilter();
}
@Bean
PostResultFilter postResultFilter() {
return new PostResultFilter();
}
@Bean
BaseZuulController baseZuulController() {
return ApiContext.getApiConfig().getBaseZuulController();
}
@PostConstruct
public void after() {
doAfter();
}
protected void doAfter() {
initMessage();
apiMetaManager.refresh();
}
protected void initMessage() {
ErrorFactory.initMessageSource(ApiContext.getApiConfig().getI18nModules());
}
}

@ -0,0 +1,43 @@
package com.gitee.sop.gatewaycommon.zuul.configuration;
import com.gitee.sop.gatewaycommon.bean.ApiContext;
import com.gitee.sop.gatewaycommon.result.ResultExecutor;
import com.netflix.zuul.context.RequestContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* 处理网关自身异常
*
* @author tanghc
*/
@Controller
@Slf4j
public class BaseZuulController implements ErrorController {
public static final String ERROR_PATH = "/error";
/**
* 错误最终会到这里来
*/
@RequestMapping(ERROR_PATH)
@ResponseBody
public Object error() {
RequestContext ctx = RequestContext.getCurrentContext();
Throwable throwable = ctx.getThrowable();
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_PATH;
}
}

@ -0,0 +1,23 @@
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());
}
}

@ -0,0 +1,117 @@
package com.gitee.sop.gatewaycommon.zuul.filter;
import com.alibaba.fastjson.JSON;
import com.gitee.sop.gatewaycommon.result.ApiResult;
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());
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
* @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));
}
public static void main(String[] args) {
System.out.println(JSON.toJSONString(new ApiResult()));
}
/**
* to classify a filter by type. Standard types in Zuul are "pre" for pre-routing filtering,
* "route" 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("route"),
/** 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;
}
}
}

@ -0,0 +1,60 @@
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;
}
}

@ -0,0 +1,48 @@
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.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.apache.commons.io.IOUtils;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
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 {
InputStream responseDataStream = requestContext.getResponseDataStream();
ApiConfig apiConfig = ApiContext.getApiConfig();
ResultExecutor<RequestContext, String> resultExecutor = apiConfig.getZuulResultExecutor();
String serviceResult;
try {
serviceResult = IOUtils.toString(responseDataStream, SopConstants.CHARSET_UTF8);
} catch (Exception e) {
log.error("业务方无数据返回", e);
serviceResult = SopConstants.EMPTY_JSON;
}
String finalResult = resultExecutor.mergeResult(requestContext, serviceResult);
requestContext.setResponseBody(finalResult);
return null;
}
}

@ -0,0 +1,41 @@
package com.gitee.sop.gatewaycommon.zuul.filter;
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import javax.servlet.http.HttpServletRequest;
/**
* @author tanghc
*/
public class PreTokenFilter extends BaseZuulFilter {
@Override
protected FilterType getFilterType() {
return FilterType.PRE;
}
@Override
protected int getFilterOrder() {
return FilterConstants.PRE_DECORATION_FILTER_ORDER + 1;
}
@Override
protected Object doRun(RequestContext ctx) throws ZuulException {
Object serviceId = ctx.get(FilterConstants.SERVICE_ID_KEY);
log.info("serviceId:{}", serviceId);
HttpServletRequest request = ctx.getRequest();
log.info("send {} request to {}", request.getMethod(), request.getRequestURL().toString());
String accessToken = request.getParameter("access_token");
if (StringUtils.isBlank(accessToken)) {
throw ErrorEnum.AOP_INVALID_APP_AUTH_TOKEN.getErrorMeta().getException();
}
return null;
}
}

@ -0,0 +1,48 @@
package com.gitee.sop.gatewaycommon.zuul.filter;
import com.gitee.sop.gatewaycommon.bean.ApiContext;
import com.gitee.sop.gatewaycommon.bean.ApiConfig;
import com.gitee.sop.gatewaycommon.exception.ApiException;
import com.gitee.sop.gatewaycommon.param.ApiParam;
import com.gitee.sop.gatewaycommon.validate.Validator;
import com.gitee.sop.gatewaycommon.zuul.ZuulContext;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import javax.servlet.http.HttpServletRequest;
/**
* 前置校验
* @author tanghc
*/
public class PreValidateFilter extends BaseZuulFilter {
@Override
protected FilterType getFilterType() {
return FilterType.PRE;
}
@Override
protected int getFilterOrder() {
// 在org.springframework.cloud.netflix.zuul.filters.pre.PreDecorationFilter前面
return FilterConstants.PRE_DECORATION_FILTER_ORDER - 1;
}
@Override
protected Object doRun(RequestContext requestContext) throws ZuulException {
ApiConfig apiConfig = ApiContext.getApiConfig();
// 解析参数
ApiParam param = apiConfig.getZuulParamBuilder().build(requestContext);
ZuulContext.setApiParam(param);
// 验证操作,这里有负责验证签名参数
Validator validator = apiConfig.getValidator();
try {
validator.validate(param);
} catch (ApiException e) {
log.error("验证失败,params:{}", param.toJSONString(), e);
throw e;
}
return null;
}
}

@ -0,0 +1,111 @@
package com.gitee.sop.gatewaycommon.zuul.param;
import com.alibaba.fastjson.JSON;
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
import com.gitee.sop.gatewaycommon.param.ApiParam;
import com.gitee.sop.gatewaycommon.param.ApiUploadContext;
import com.gitee.sop.gatewaycommon.param.ParamBuilder;
import com.gitee.sop.gatewaycommon.util.RequestUtil;
import com.gitee.sop.gatewaycommon.zuul.ZuulContext;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.http.HttpServletRequestWrapper;
import org.springframework.http.MediaType;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* 参数解析默认实现
*
* @author tanghc
*/
public class ZuulParamBuilder implements ParamBuilder<RequestContext> {
private static final String CONTENT_TYPE_MULTIPART = MediaType.MULTIPART_FORM_DATA_VALUE;
private static final String CONTENT_TYPE_JSON = MediaType.APPLICATION_JSON_VALUE;
private static final String CONTENT_TYPE_TEXT = MediaType.TEXT_PLAIN_VALUE;
private static final String GET = "get";
@Override
public ApiParam build(RequestContext ctx) {
try {
HttpServletRequest request = ctx.getRequest();
Map<String, Object> params = this.getJson(request);
return new ApiParam(params);
} catch (Exception e) {
throw ErrorEnum.ISV_INVALID_PARAMETER.getErrorMeta().getException();
}
}
public Map<String, Object> getJson(HttpServletRequest request) throws Exception {
// zuul会做一层包装
if (request instanceof HttpServletRequestWrapper) {
HttpServletRequestWrapper req = (HttpServletRequestWrapper) request;
request = req.getRequest();
}
Map<String, Object> params = null;
if (GET.equalsIgnoreCase(request.getMethod())) {
params = RequestUtil.convertRequestParamsToMap(request);
} else {
String contectType = request.getContentType();
if (contectType == null) {
contectType = "";
}
contectType = contectType.toLowerCase();
// json或者纯文本形式
if (contectType.contains(CONTENT_TYPE_JSON) || contectType.contains(CONTENT_TYPE_TEXT)) {
String txt = RequestUtil.getText(request);
params = JSON.parseObject(txt);
} else if (contectType.contains(CONTENT_TYPE_MULTIPART)) {
// 上传文件形式
params = this.parseUploadRequest(request);
} else {
params = RequestUtil.convertRequestParamsToMap(request);
}
}
return params;
}
/**
* 解析文件上传请求
*
* @param request
* @return 返回json字符串
*/
protected Map<String, Object> parseUploadRequest(HttpServletRequest request) {
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(
request.getSession().getServletContext());
// 检查form中是否有enctype="multipart/form-data"
if (multipartResolver.isMultipart(request)) {
MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request;
Map<String, MultipartFile> fileMap = multiRequest.getFileMap();
Map<String, MultipartFile> finalMap = new HashMap<>(fileMap.size());
Set<String> keys = fileMap.keySet();
for (String name : keys) {
MultipartFile file = fileMap.get(name);
if (file.getSize() > 0) {
finalMap.put(name, file);
}
}
if (finalMap.size() > 0) {
// 保存上传文件
ZuulContext.setUploadContext(new ApiUploadContext(finalMap));
}
}
return RequestUtil.convertRequestParamsToMap(request);
}
}

@ -0,0 +1,58 @@
package com.gitee.sop.gatewaycommon.zuul.result;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
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.result.BaseExecutorAdapter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
/**
* @author tanghc
*/
@Slf4j
public class ZuulResultExecutor extends BaseExecutorAdapter<RequestContext, String> {
@Override
public int getBizHeaderCode(RequestContext requestContext) {
HttpServletResponse response = requestContext.getResponse();
int code = HttpStatus.OK.value();
String bizErrorCode = response.getHeader(SopConstants.X_BIZ_ERROR_CODE);
if (bizErrorCode != null) {
code = Integer.valueOf(bizErrorCode);
}
return code;
}
@Override
public Map<String, ?> getApiParam(RequestContext requestContext) {
return (Map<String, ?>) requestContext.get(SopConstants.CACHE_API_PARAM);
}
@Override
public String buildErrorResult(RequestContext request, 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();
}
}
if (error == null) {
error = ErrorEnum.AOP_UNKNOW_ERROR.getErrorMeta().getError();
}
JSONObject jsonObject = (JSONObject) JSON.toJSON(error);
return this.merge(request, jsonObject);
}
}

@ -0,0 +1,46 @@
package com.gitee.sop.gatewaycommon.zuul.route;
import com.gitee.sop.gatewaycommon.manager.RouteRepository;
import com.gitee.sop.gatewaycommon.param.ApiParam;
import com.gitee.sop.gatewaycommon.zuul.ZuulContext;
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;
/**
* @author tanghc
*/
public class SopRouteLocator implements RouteLocator, Ordered {
private RouteRepository<Route> routeRepository;
public SopRouteLocator(RouteRepository<Route> routeRepository) {
this.routeRepository = routeRepository;
}
@Override
public Collection<String> getIgnoredPaths() {
return Collections.emptyList();
}
@Override
public List<Route> getRoutes() {
return null;
}
@Override
public Route getMatchingRoute(String path) {
ApiParam param = ZuulContext.getApiParam();
String nameVersion = param.fetchNameVersion();
return routeRepository.get(nameVersion);
}
@Override
public int getOrder() {
return 0;
}
}

@ -0,0 +1,14 @@
package com.gitee.sop.gatewaycommon.zuul.route;
import com.gitee.sop.gatewaycommon.bean.BaseRouteDefinition;
import lombok.Getter;
import lombok.Setter;
/**
* @author tanghc
*/
@Getter
@Setter
public class ZuulRouteDefinition extends BaseRouteDefinition {
private String path;
}

@ -0,0 +1,41 @@
package com.gitee.sop.gatewaycommon.zuul.route;
import com.gitee.sop.gatewaycommon.manager.RouteRepository;
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
import org.springframework.cloud.netflix.zuul.filters.Route;
import java.util.HashMap;
import java.util.Map;
/**
* @author tanghc
*/
public class ZuulRouteRepository implements RouteRepository<Route> {
/** key:nameVersion */
private Map<String, Route> nameVersionServiceIdMap = new HashMap<>(128);
@Override
public Route get(String id) {
Route route = nameVersionServiceIdMap.get(id);
if (route == null) {
throw ErrorEnum.ISV_INVALID_METHOD.getErrorMeta().getException();
}
return route;
}
@Override
public String add(Route route) {
return this.update(route);
}
@Override
public String update(Route route) {
nameVersionServiceIdMap.put(route.getId(), route);
return null;
}
@Override
public void delete(String id) {
nameVersionServiceIdMap.remove(id);
}
}

@ -0,0 +1,12 @@
package com.gitee.sop.gatewaycommon.zuul.route;
import com.gitee.sop.gatewaycommon.bean.BaseServiceRouteInfo;
import com.gitee.sop.gatewaycommon.gateway.route.GatewayRouteDefinition;
import lombok.Data;
/**
* @author thc
*/
@Data
public class ZuulServiceRouteInfo extends BaseServiceRouteInfo<ZuulRouteDefinition> {
}

@ -0,0 +1,33 @@
package com.gitee.sop.gatewaycommon.zuul.route;
import com.gitee.sop.gatewaycommon.manager.BaseRouteManager;
import com.gitee.sop.gatewaycommon.manager.RouteRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.netflix.zuul.filters.Route;
import org.springframework.core.env.Environment;
/**
* @author tanghc
*/
@Slf4j
public class ZuulZookeeperRouteManager extends BaseRouteManager<ZuulRouteDefinition, ZuulServiceRouteInfo, Route> {
public ZuulZookeeperRouteManager(Environment environment, RouteRepository<Route> routeRepository) {
super(environment, routeRepository);
}
@Override
protected Class<ZuulServiceRouteInfo> getServiceRouteInfoClass() {
return ZuulServiceRouteInfo.class;
}
@Override
protected Class<ZuulRouteDefinition> getRouteDefinitionClass() {
return ZuulRouteDefinition.class;
}
@Override
protected Route buildRouteDefinition(ZuulServiceRouteInfo serviceRouteInfo, ZuulRouteDefinition routeDefinition) {
return new Route(routeDefinition.getId(), routeDefinition.getPath(), serviceRouteInfo.getAppName(), null, false, null);
}
}

@ -26,10 +26,29 @@
<version>1.0.0-SNAPSHOT</version>
</dependency>
<!--<dependency>-->
<!--<groupId>org.springframework.cloud</groupId>-->
<!--<artifactId>spring-cloud-starter-netflix-zuul</artifactId>-->
<!--</dependency>-->
<!-- ↓↓↓ 使用spring cloud zuul ↓↓↓
如果要使用gateway需要注释掉
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
-->
<!-- ↑↑↑ 使用spring cloud zuul ↑↑↑ -->
<!-- ↓↓↓ 使用spring cloud gateway ↓↓↓
如果要使用zuul需要注释掉
-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<!-- ↑↑↑ 使用spring cloud gateway ↑↑↑ -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>

@ -1,8 +1,7 @@
package com.gitee.sop.gateway.config;
import com.gitee.sop.gatewaycommon.bean.ApiContext;
import com.gitee.sop.gatewaycommon.configuration.AlipayGatewayConfiguration;
import com.gitee.sop.gatewaycommon.configuration.TaobaoGatewayConfiguration;
import com.gitee.sop.gatewaycommon.gateway.configuration.AlipayGatewayConfiguration;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
@ -12,25 +11,25 @@ import java.util.Map;
* 开通支付宝开放平台能力
* @author tanghc
*/
//@Configuration
//public class GatewayConfig extends AlipayGatewayConfiguration {
//
// {
// Map<String, String> appSecretStore = new HashMap();
// appSecretStore.put("alipay_test", "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlyb9aUBaljQP/vjmBFe1mF8HsWSvyfC2NTlpT/V9E+sBxTr8TSkbzJCeeeOEm4LCaVXL0Qz63MZoT24v7AIXTuMdj4jyiM/WJ4tjrWAgnmohNOegfntTto16C3l234vXz4ryWZMR/7W+MXy5B92wPGQEJ0LKFwNEoLspDEWZ7RdE53VH7w6y6sIZUfK+YkXWSwehfKPKlx+lDw3zRJ3/yvMF+U+BAdW/MfECe1GuBnCFKnlMRh3UKczWyXWkL6ItOpYHHJi/jx85op5BWDje2pY9QowzfN94+0DB3T7UvZeweu3zlP6diwAJDzLaFQX8ULfWhY+wfKxIRgs9NoiSAQIDAQAB");
// ApiContext.getApiConfig().addAppSecret(appSecretStore);
// }
//}
/**
* 开通淘宝开放平能力
*/
@Configuration
public class GatewayConfig extends TaobaoGatewayConfiguration {
public class GatewayConfig extends AlipayGatewayConfiguration {
{
Map<String, String> appSecretStore = new HashMap();
appSecretStore.put("taobao_test", "G9w0BAQEFAAOCAQ8AMIIBCgKCA");
appSecretStore.put("alipay_test", "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlyb9aUBaljQP/vjmBFe1mF8HsWSvyfC2NTlpT/V9E+sBxTr8TSkbzJCeeeOEm4LCaVXL0Qz63MZoT24v7AIXTuMdj4jyiM/WJ4tjrWAgnmohNOegfntTto16C3l234vXz4ryWZMR/7W+MXy5B92wPGQEJ0LKFwNEoLspDEWZ7RdE53VH7w6y6sIZUfK+YkXWSwehfKPKlx+lDw3zRJ3/yvMF+U+BAdW/MfECe1GuBnCFKnlMRh3UKczWyXWkL6ItOpYHHJi/jx85op5BWDje2pY9QowzfN94+0DB3T7UvZeweu3zlP6diwAJDzLaFQX8ULfWhY+wfKxIRgs9NoiSAQIDAQAB");
ApiContext.getApiConfig().addAppSecret(appSecretStore);
}
}
/**
* 开通淘宝开放平能力
*/
//@Configuration
//public class GatewayConfig extends TaobaoGatewayConfiguration {
//
// {
// Map<String, String> appSecretStore = new HashMap();
// appSecretStore.put("taobao_test", "G9w0BAQEFAAOCAQ8AMIIBCgKCA");
// ApiContext.getApiConfig().addAppSecret(appSecretStore);
// }
//}

@ -8,16 +8,16 @@ import org.springframework.context.annotation.Configuration;
* 使用支付宝开放平台功能
* @author tanghc
*/
//@Configuration
//public class OpenServiceConfig extends AlipayServiceConfiguration {
//
//}
@Configuration
public class OpenServiceConfig extends AlipayServiceConfiguration {
}
/**
* 使用淘宝开放平台功能
* @author tanghc
*/
@Configuration
public class OpenServiceConfig extends TaobaoServiceConfiguration {
}
//@Configuration
//public class OpenServiceConfig extends TaobaoServiceConfiguration {
//
//}

Loading…
Cancel
Save