mirror of
https://gitee.com/durcframework/SOP.git
synced 2025-08-11 21:57:56 +08:00
gateway init
This commit is contained in:
2
pom.xml
2
pom.xml
@@ -12,7 +12,7 @@
|
|||||||
<module>sop-registry</module>
|
<module>sop-registry</module>
|
||||||
<module>sop-gateway</module>
|
<module>sop-gateway</module>
|
||||||
<module>sop-gateway-common</module>
|
<module>sop-gateway-common</module>
|
||||||
<module>sop-server-common</module>
|
<module>sop-service-common</module>
|
||||||
<module>sop-story</module>
|
<module>sop-story</module>
|
||||||
<module>sop-book</module>
|
<module>sop-book</module>
|
||||||
<module>sop-test</module>
|
<module>sop-test</module>
|
||||||
|
@@ -45,10 +45,52 @@
|
|||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
<!--<dependency>-->
|
||||||
|
<!--<groupId>org.springframework.cloud</groupId>-->
|
||||||
|
<!--<artifactId>spring-cloud-starter-netflix-zuul</artifactId>-->
|
||||||
|
<!--</dependency>-->
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.cloud</groupId>
|
<groupId>org.springframework.cloud</groupId>
|
||||||
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
|
<artifactId>spring-cloud-starter-gateway</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-webflux</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- zookeeper -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-zookeeper-all</artifactId>
|
||||||
|
<exclusions>
|
||||||
|
<!-- 不使用服务发现 -->
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.apache.zookeeper</groupId>
|
||||||
|
<artifactId>zookeeper</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.zookeeper</groupId>
|
||||||
|
<artifactId>zookeeper</artifactId>
|
||||||
|
<version>3.4.12</version>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-log4j12</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-test</artifactId>
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
@@ -70,6 +112,11 @@
|
|||||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- commons -->
|
<!-- commons -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-lang3</artifactId>
|
||||||
|
<version>3.8.1</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>commons-collections</groupId>
|
<groupId>commons-collections</groupId>
|
||||||
<artifactId>commons-collections</artifactId>
|
<artifactId>commons-collections</artifactId>
|
||||||
@@ -96,6 +143,18 @@
|
|||||||
<version>1.18.4</version>
|
<version>1.18.4</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.servlet</groupId>
|
||||||
|
<artifactId>javax.servlet-api</artifactId>
|
||||||
|
<version>3.1.0</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-netflix-ribbon</artifactId>
|
||||||
|
<version>2.1.0.RELEASE</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
@@ -1,13 +1,9 @@
|
|||||||
package com.gitee.sop.gatewaycommon.bean;
|
package com.gitee.sop.gatewaycommon.bean;
|
||||||
|
|
||||||
import com.gitee.sop.gatewaycommon.configuration.BaseZuulController;
|
import com.gitee.sop.gatewaycommon.param.ApiParamBuilder;
|
||||||
import com.gitee.sop.gatewaycommon.param.ApiParamParser;
|
import com.gitee.sop.gatewaycommon.param.ParamBuilder;
|
||||||
import com.gitee.sop.gatewaycommon.param.ParamParser;
|
|
||||||
import com.gitee.sop.gatewaycommon.result.ApiResultExecutor;
|
import com.gitee.sop.gatewaycommon.result.ApiResultExecutor;
|
||||||
import com.gitee.sop.gatewaycommon.result.JsonResultSerializer;
|
|
||||||
import com.gitee.sop.gatewaycommon.result.ResultExecutor;
|
import com.gitee.sop.gatewaycommon.result.ResultExecutor;
|
||||||
import com.gitee.sop.gatewaycommon.result.ResultSerializer;
|
|
||||||
import com.gitee.sop.gatewaycommon.result.XmlResultSerializer;
|
|
||||||
import com.gitee.sop.gatewaycommon.secret.AppSecretManager;
|
import com.gitee.sop.gatewaycommon.secret.AppSecretManager;
|
||||||
import com.gitee.sop.gatewaycommon.secret.CacheAppSecretManager;
|
import com.gitee.sop.gatewaycommon.secret.CacheAppSecretManager;
|
||||||
import com.gitee.sop.gatewaycommon.session.ApiSessionManager;
|
import com.gitee.sop.gatewaycommon.session.ApiSessionManager;
|
||||||
@@ -40,15 +36,6 @@ public class ApiConfig {
|
|||||||
*/
|
*/
|
||||||
private ResultExecutor resultExecutor = new ApiResultExecutor();
|
private ResultExecutor resultExecutor = new ApiResultExecutor();
|
||||||
|
|
||||||
/**
|
|
||||||
* json序列化
|
|
||||||
*/
|
|
||||||
private ResultSerializer jsonResultSerializer = new JsonResultSerializer();
|
|
||||||
/**
|
|
||||||
* xml序列化
|
|
||||||
*/
|
|
||||||
private ResultSerializer xmlResultSerializer = new XmlResultSerializer();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* app秘钥管理
|
* app秘钥管理
|
||||||
*/
|
*/
|
||||||
@@ -65,9 +52,9 @@ public class ApiConfig {
|
|||||||
private Signer signer = new ApiSigner();
|
private Signer signer = new ApiSigner();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 参数解析
|
* 参数解析,gateway
|
||||||
*/
|
*/
|
||||||
private ParamParser paramParser = new ApiParamParser();
|
private ParamBuilder paramBuilder = new ApiParamBuilder();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 验证
|
* 验证
|
||||||
@@ -84,12 +71,6 @@ public class ApiConfig {
|
|||||||
*/
|
*/
|
||||||
private List<String> i18nModules = new ArrayList<String>();
|
private List<String> i18nModules = new ArrayList<String>();
|
||||||
|
|
||||||
/**
|
|
||||||
* 基础Controller
|
|
||||||
*/
|
|
||||||
private BaseZuulController baseZuulController = new BaseZuulController();
|
|
||||||
|
|
||||||
|
|
||||||
// -------- fields ---------
|
// -------- fields ---------
|
||||||
/**
|
/**
|
||||||
* 忽略验证
|
* 忽略验证
|
||||||
|
@@ -1,149 +1,29 @@
|
|||||||
package com.gitee.sop.gatewaycommon.bean;
|
package com.gitee.sop.gatewaycommon.bean;
|
||||||
|
|
||||||
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 com.gitee.sop.gatewaycommon.session.SessionManager;
|
||||||
|
|
||||||
import javax.servlet.ServletContext;
|
//import com.netflix.zuul.context.RequestContext;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
import javax.servlet.http.HttpSession;
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 应用上下文,方便获取信息
|
* 应用上下文,方便获取信息
|
||||||
*
|
*
|
||||||
* @author tanghc
|
* @author tanghc
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public class ApiContext {
|
public class ApiContext {
|
||||||
private static final String ATTR_PARAM = "zuul.common.api.param";
|
|
||||||
private static final String ATTR_UPLOAD_CONTEXT = "zuul.common.api.upload_context";
|
|
||||||
|
|
||||||
private ApiContext(){}
|
private ApiContext() {
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取随机码
|
|
||||||
* @return 返回随机码
|
|
||||||
*/
|
|
||||||
public static String getRandomKey() {
|
|
||||||
HttpSession session = getSession();
|
|
||||||
if (session == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return (String) session.getAttribute(SopConstants.RANDOM_KEY_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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取session管理器
|
* 获取session管理器
|
||||||
|
*
|
||||||
* @return 返回SessionManager
|
* @return 返回SessionManager
|
||||||
*/
|
*/
|
||||||
public static SessionManager getSessionManager() {
|
public static SessionManager getSessionManager() {
|
||||||
return getApiConfig().getSessionManager();
|
return getApiConfig().getSessionManager();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 返回自定义的session,被SessionManager管理
|
|
||||||
*
|
|
||||||
* @return 如果sessionId为null,则返回null
|
|
||||||
*/
|
|
||||||
public static HttpSession getManagedSession() {
|
|
||||||
String sessionId = getSessionId();
|
|
||||||
if (sessionId != null) {
|
|
||||||
return getSessionManager().getSession(sessionId);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 同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() {
|
public static ApiConfig getApiConfig() {
|
||||||
return ApiConfig.getInstance();
|
return ApiConfig.getInstance();
|
||||||
@@ -153,34 +33,4 @@ public class ApiContext {
|
|||||||
ApiConfig.setInstance(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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,17 +0,0 @@
|
|||||||
package com.gitee.sop.gatewaycommon.bean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author tanghc
|
|
||||||
*/
|
|
||||||
public enum RequestMode {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 数字签名请求模式
|
|
||||||
*/
|
|
||||||
SIGNATURE,
|
|
||||||
/**
|
|
||||||
* 公私钥加密模式,这样请求和返回的数据都经过加密处理
|
|
||||||
*/
|
|
||||||
ENCRYPT
|
|
||||||
|
|
||||||
}
|
|
@@ -8,9 +8,9 @@ import java.nio.charset.StandardCharsets;
|
|||||||
*/
|
*/
|
||||||
public class SopConstants {
|
public class SopConstants {
|
||||||
|
|
||||||
public static final String NULL = "null";
|
|
||||||
public static final String RANDOM_KEY_NAME = "ssl_randomKey";
|
public static final String RANDOM_KEY_NAME = "ssl_randomKey";
|
||||||
public static final Charset CHARSET_UTF8 = StandardCharsets.UTF_8;
|
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_JSON = "json";
|
||||||
public static final String FORMAT_XML = "xml";
|
public static final String FORMAT_XML = "xml";
|
||||||
public static final String AUTHORIZATION = "Authorization";
|
public static final String AUTHORIZATION = "Authorization";
|
||||||
@@ -30,4 +30,21 @@ public class SopConstants {
|
|||||||
|
|
||||||
public static final String REST_PARAM_NAME = "_REST_PARAM_NAME_";
|
public static final String REST_PARAM_NAME = "_REST_PARAM_NAME_";
|
||||||
public static final String REST_PARAM_VERSION = "_REST_PARAM_VERSION_";
|
public static final String REST_PARAM_VERSION = "_REST_PARAM_VERSION_";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在拦截器中调用获取参数:
|
||||||
|
* String cachedBody = (String)exchange.getAttribute(SopConstants.CACHE_REQUEST_BODY_OBJECT_KEY);
|
||||||
|
*/
|
||||||
|
public static final String CACHE_REQUEST_BODY_OBJECT_KEY = "cachedRequestBodyObject";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在拦截器中调用获取参数:
|
||||||
|
* Map<String, String> params = exchange.getAttribute(SopConstants.CACHE_REQUEST_BODY_FOR_MAP);
|
||||||
|
*/
|
||||||
|
public static final String CACHE_REQUEST_BODY_FOR_MAP = "cacheRequestBodyForMap";
|
||||||
|
|
||||||
|
public static final String CACHE_API_PARAM = "cacheApiParam";
|
||||||
|
|
||||||
|
public static final String X_BIZ_ERROR_CODE = "x-biz-error-code";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -8,7 +8,7 @@ import com.gitee.sop.gatewaycommon.validate.alipay.AlipaySigner;
|
|||||||
*
|
*
|
||||||
* @author tanghc
|
* @author tanghc
|
||||||
*/
|
*/
|
||||||
public class AlipayZuulConfiguration extends BaseZuulConfiguration {
|
public class AlipayGatewayConfiguration extends BaseGatewayConfiguration {
|
||||||
|
|
||||||
static {
|
static {
|
||||||
ApiContext.getApiConfig().setSigner(new AlipaySigner());
|
ApiContext.getApiConfig().setSigner(new AlipaySigner());
|
@@ -0,0 +1,118 @@
|
|||||||
|
package com.gitee.sop.gatewaycommon.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.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;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Primary;
|
||||||
|
import org.springframework.core.Ordered;
|
||||||
|
import org.springframework.core.annotation.Order;
|
||||||
|
import org.springframework.core.env.Environment;
|
||||||
|
import org.springframework.http.codec.ServerCodecConfigurer;
|
||||||
|
import org.springframework.web.reactive.result.view.ViewResolver;
|
||||||
|
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
public class BaseGatewayConfiguration {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
protected Environment environment;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
protected GatewayZookeeperApiMetaManager gatewayZookeeperApiMetaManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义异常处理[@@]注册Bean时依赖的Bean,会从容器中直接获取,所以直接注入即可
|
||||||
|
*
|
||||||
|
* @param viewResolversProvider
|
||||||
|
* @param serverCodecConfigurer
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Primary
|
||||||
|
@Bean
|
||||||
|
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||||
|
public ErrorWebExceptionHandler errorWebExceptionHandler(ObjectProvider<List<ViewResolver>> viewResolversProvider,
|
||||||
|
ServerCodecConfigurer serverCodecConfigurer) {
|
||||||
|
|
||||||
|
GatewayExceptionHandler jsonExceptionHandler = new GatewayExceptionHandler();
|
||||||
|
jsonExceptionHandler.setViewResolvers(viewResolversProvider.getIfAvailable(Collections::emptyList));
|
||||||
|
jsonExceptionHandler.setMessageWriters(serverCodecConfigurer.getWriters());
|
||||||
|
jsonExceptionHandler.setMessageReaders(serverCodecConfigurer.getReaders());
|
||||||
|
return jsonExceptionHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
GatewayModifyResponseGatewayFilter gatewayModifyResponseGatewayFilter() {
|
||||||
|
return new GatewayModifyResponseGatewayFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
ReadBodyRoutePredicateFactory readBodyRoutePredicateFactory() {
|
||||||
|
return new ReadBodyRoutePredicateFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
NameVersionRoutePredicateFactory paramRoutePredicateFactory() {
|
||||||
|
return new NameVersionRoutePredicateFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
ValidateFilter validateFilter() {
|
||||||
|
return new ValidateFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
LoadBalancerClientExtFilter loadBalancerClientExtFilter() {
|
||||||
|
return new LoadBalancerClientExtFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
GatewayZookeeperApiMetaManager zookeeperApiMetaManager(Environment environment, DynamicRouteServiceManager dynamicRouteServiceManager) {
|
||||||
|
return new GatewayZookeeperApiMetaManager(environment, dynamicRouteServiceManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
DynamicRouteServiceManager dynamicRouteServiceManager() {
|
||||||
|
return new DynamicRouteServiceManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void after() {
|
||||||
|
doAfter();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void doAfter() {
|
||||||
|
initMessage();
|
||||||
|
gatewayZookeeperApiMetaManager.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void initMessage() {
|
||||||
|
ErrorFactory.initMessageSource(ApiContext.getApiConfig().getI18nModules());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -1,112 +0,0 @@
|
|||||||
package com.gitee.sop.gatewaycommon.configuration;
|
|
||||||
|
|
||||||
import com.gitee.sop.gatewaycommon.bean.ApiContext;
|
|
||||||
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.ApiMetaChangeListener;
|
|
||||||
import com.gitee.sop.gatewaycommon.manager.ApiMetaContext;
|
|
||||||
import com.gitee.sop.gatewaycommon.manager.ApiMetaManager;
|
|
||||||
import com.gitee.sop.gatewaycommon.manager.DefaultApiMetaContext;
|
|
||||||
import com.gitee.sop.gatewaycommon.manager.DefaultApiMetaManager;
|
|
||||||
import com.gitee.sop.gatewaycommon.manager.SopRouteLocator;
|
|
||||||
import com.gitee.sop.gatewaycommon.message.ErrorFactory;
|
|
||||||
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.data.redis.core.StringRedisTemplate;
|
|
||||||
import org.springframework.data.redis.listener.PatternTopic;
|
|
||||||
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
|
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author tanghc
|
|
||||||
*/
|
|
||||||
public class BaseZuulConfiguration {
|
|
||||||
|
|
||||||
public static final String API_CHANGE_CHANNEL = "channel.sop.api.change";
|
|
||||||
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
protected ZuulProperties zuulProperties;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
protected ServerProperties server;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
protected ApiMetaManager apiMetaManager;
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public ApiMetaContext apiMetaContext() {
|
|
||||||
return new DefaultApiMetaContext();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public ApiMetaManager apiMetaManager(StringRedisTemplate stringRedisTemplate, ApiMetaContext apiMetaContext) {
|
|
||||||
return new DefaultApiMetaManager(stringRedisTemplate, apiMetaContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
PreValidateFilter preValidateFilter() {
|
|
||||||
return new PreValidateFilter();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public PreDecorationFilter preDecorationFilter(ApiMetaContext apiMetaContext, ProxyRequestHelper proxyRequestHelper) {
|
|
||||||
SopRouteLocator routeLocator = new SopRouteLocator(apiMetaContext);
|
|
||||||
return new PreDecorationFilter(routeLocator,
|
|
||||||
this.server.getServlet().getContextPath(),
|
|
||||||
this.zuulProperties,
|
|
||||||
proxyRequestHelper);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
ErrorFilter errorFilter() {
|
|
||||||
return new ErrorFilter();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
PostResultFilter postResultFilter() {
|
|
||||||
return new PostResultFilter();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 配置redis事件订阅
|
|
||||||
*
|
|
||||||
* @param apiMetaManager
|
|
||||||
* @param redisTemplate
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
@Bean
|
|
||||||
RedisMessageListenerContainer container(ApiMetaManager apiMetaManager, StringRedisTemplate redisTemplate) {
|
|
||||||
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
|
|
||||||
container.setConnectionFactory(redisTemplate.getConnectionFactory());
|
|
||||||
ApiMetaChangeListener apiMetaChangeListener = new ApiMetaChangeListener(apiMetaManager, redisTemplate);
|
|
||||||
container.addMessageListener(apiMetaChangeListener, new PatternTopic(API_CHANGE_CHANNEL));
|
|
||||||
return container;
|
|
||||||
}
|
|
||||||
|
|
||||||
@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());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -1,46 +0,0 @@
|
|||||||
package com.gitee.sop.gatewaycommon.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;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 处理网关自身异常
|
|
||||||
*
|
|
||||||
* @author tanghc
|
|
||||||
*/
|
|
||||||
@Controller
|
|
||||||
@Slf4j
|
|
||||||
public class BaseZuulController implements ErrorController {
|
|
||||||
|
|
||||||
public static final String ERROR_PATH = "/error";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 错误最终会到这里来
|
|
||||||
*/
|
|
||||||
@RequestMapping(ERROR_PATH)
|
|
||||||
@ResponseBody
|
|
||||||
public Object error(HttpServletRequest request, HttpServletResponse response) {
|
|
||||||
RequestContext ctx = RequestContext.getCurrentContext();
|
|
||||||
Throwable throwable = ctx.getThrowable();
|
|
||||||
return this.buildResult(throwable);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Object buildResult(Throwable throwable) {
|
|
||||||
ResultExecutor resultExecutor = ApiContext.getApiConfig().getResultExecutor();
|
|
||||||
return resultExecutor.mergeError(throwable);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getErrorPath() {
|
|
||||||
return ERROR_PATH;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -9,7 +9,7 @@ import com.gitee.sop.gatewaycommon.validate.taobao.TaobaoSigner;
|
|||||||
* 淘宝开放平台:http://open.taobao.com/doc.htm
|
* 淘宝开放平台:http://open.taobao.com/doc.htm
|
||||||
* @author tanghc
|
* @author tanghc
|
||||||
*/
|
*/
|
||||||
public class TaobaoZuulConfiguration extends BaseZuulConfiguration {
|
public class TaobaoGatewayConfiguration extends BaseGatewayConfiguration {
|
||||||
|
|
||||||
static {
|
static {
|
||||||
ParamNames.APP_KEY_NAME = "app_key";
|
ParamNames.APP_KEY_NAME = "app_key";
|
@@ -1,20 +0,0 @@
|
|||||||
package com.gitee.sop.gatewaycommon.easyopen;
|
|
||||||
|
|
||||||
import org.springframework.cloud.netflix.zuul.filters.Route;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author tanghc
|
|
||||||
*/
|
|
||||||
public class EasyopenRoute extends Route {
|
|
||||||
public EasyopenRoute(String id, String path, String location, String prefix, Boolean retryable, Set<String> ignoredHeaders) {
|
|
||||||
super(id, path, location, prefix, retryable, ignoredHeaders);
|
|
||||||
}
|
|
||||||
|
|
||||||
public EasyopenRoute(String id, String location) {
|
|
||||||
this(id, "/" + location + "/", location, "", false, Collections.emptySet());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -1,74 +0,0 @@
|
|||||||
package com.gitee.sop.gatewaycommon.easyopen;
|
|
||||||
|
|
||||||
import com.gitee.sop.gatewaycommon.filter.ErrorFilter;
|
|
||||||
import com.gitee.sop.gatewaycommon.filter.PostResultFilter;
|
|
||||||
import com.gitee.sop.gatewaycommon.easyopen.filter.PostEasyopenResultFilter;
|
|
||||||
import com.gitee.sop.gatewaycommon.filter.PreValidateFilter;
|
|
||||||
import com.gitee.sop.gatewaycommon.manager.ApiMetaContext;
|
|
||||||
import com.gitee.sop.gatewaycommon.manager.ApiMetaManager;
|
|
||||||
import com.gitee.sop.gatewaycommon.manager.DefaultApiMetaContext;
|
|
||||||
import com.gitee.sop.gatewaycommon.manager.DefaultApiMetaManager;
|
|
||||||
import com.gitee.sop.gatewaycommon.manager.SopRouteLocator;
|
|
||||||
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.data.redis.core.StringRedisTemplate;
|
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author tanghc
|
|
||||||
*/
|
|
||||||
public class EasyopenZuulConfig {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
protected ZuulProperties zuulProperties;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
protected ServerProperties server;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
protected ApiMetaManager apiMetaManager;
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public ApiMetaContext apiMetaContext() {
|
|
||||||
return new DefaultApiMetaContext();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public ApiMetaManager apiMetaManager(StringRedisTemplate stringRedisTemplate, ApiMetaContext apiMetaContext) {
|
|
||||||
return new DefaultApiMetaManager(stringRedisTemplate, apiMetaContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public PreDecorationFilter preDecorationFilter(ApiMetaContext apiMetaContext, ProxyRequestHelper proxyRequestHelper) {
|
|
||||||
SopRouteLocator routeLocator = new SopRouteLocator(apiMetaContext);
|
|
||||||
return new PreDecorationFilter(routeLocator,
|
|
||||||
this.server.getServlet().getContextPath(),
|
|
||||||
this.zuulProperties,
|
|
||||||
proxyRequestHelper);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
PreValidateFilter preValidateFilter() {
|
|
||||||
return new PreValidateFilter();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
ErrorFilter errorFilter() {
|
|
||||||
return new ErrorFilter();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
PostResultFilter postResultFilter() {
|
|
||||||
return new PostEasyopenResultFilter();
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostConstruct
|
|
||||||
public void after() {
|
|
||||||
apiMetaManager.refresh();
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,46 +0,0 @@
|
|||||||
package com.gitee.sop.gatewaycommon.easyopen.filter;
|
|
||||||
|
|
||||||
import com.gitee.sop.gatewaycommon.filter.PostResultFilter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 合并微服务结果,统一返回格式
|
|
||||||
*
|
|
||||||
* @author tanghc
|
|
||||||
*/
|
|
||||||
public class PostEasyopenResultFilter extends PostResultFilter {
|
|
||||||
private static final String EASYOPEN_SUCCESS_CODE = "0";
|
|
||||||
//
|
|
||||||
// protected Result processServiceResult(InputStream responseDataStream, ResultBuilder resultBuilder) throws IOException {
|
|
||||||
// String responseBody = IOUtils.toString(responseDataStream, SopConstants.CHARSET_UTF8);
|
|
||||||
// ApiResult result = this.parseApiResult(responseBody);
|
|
||||||
// Result finalResult;
|
|
||||||
// if (EASYOPEN_SUCCESS_CODE.equals(result.getCode())) {
|
|
||||||
// finalResult = resultBuilder.buildSuccessResult(GATEWAY_SUCCESS_CODE, null, result.getData());
|
|
||||||
// } else {
|
|
||||||
// // 业务出错
|
|
||||||
// finalResult = resultBuilder.buildServiceError(result.getCode(), result.getMsg());
|
|
||||||
// }
|
|
||||||
// return finalResult;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// protected ApiResult parseApiResult(String responseBody) {
|
|
||||||
// ApiParam apiParam = ApiContext.getApiParam();
|
|
||||||
// String format = apiParam.fetchFormat();
|
|
||||||
// return SopConstants.FORMAT_XML.equalsIgnoreCase(format)
|
|
||||||
// ? XmlUtil.unserialize(responseBody, ApiResult.class)
|
|
||||||
// : JSON.parseObject(responseBody, ApiResult.class);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// protected ResultSerializer buildResultSerializer(HttpServletRequest request, ApiConfig apiConfig) {
|
|
||||||
// ApiParam apiParam = ApiContext.getApiParam();
|
|
||||||
// String format = apiParam.fetchFormat();
|
|
||||||
// if (SopConstants.FORMAT_JSON.equalsIgnoreCase(format)) {
|
|
||||||
// return apiConfig.getJsonResultSerializer();
|
|
||||||
// } else if (SopConstants.FORMAT_XML.equalsIgnoreCase(format)) {
|
|
||||||
// // xml格式输出
|
|
||||||
// return apiConfig.getXmlResultSerializer();
|
|
||||||
// } else {
|
|
||||||
// throw ErrorEnum.isv_invalid_format.getErrorMeta().getException();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
|
@@ -1,117 +0,0 @@
|
|||||||
package com.gitee.sop.gatewaycommon.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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,59 +0,0 @@
|
|||||||
package com.gitee.sop.gatewaycommon.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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @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,131 @@
|
|||||||
|
package com.gitee.sop.gatewaycommon.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;
|
||||||
|
import org.springframework.core.Ordered;
|
||||||
|
import org.springframework.core.io.buffer.DataBuffer;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseCookie;
|
||||||
|
import org.springframework.http.client.reactive.ClientHttpResponse;
|
||||||
|
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
|
||||||
|
import org.springframework.util.MultiValueMap;
|
||||||
|
import org.springframework.web.reactive.function.BodyInserter;
|
||||||
|
import org.springframework.web.reactive.function.BodyInserters;
|
||||||
|
import org.springframework.web.reactive.function.client.ExchangeStrategies;
|
||||||
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
|
import reactor.core.publisher.Flux;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR;
|
||||||
|
|
||||||
|
public class GatewayModifyResponseGatewayFilter implements GlobalFilter, Ordered {
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||||
|
|
||||||
|
ServerHttpResponseDecorator responseDecorator = new ServerHttpResponseDecorator(exchange.getResponse()) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
|
||||||
|
|
||||||
|
Class inClass = String.class;
|
||||||
|
Class outClass = String.class;
|
||||||
|
|
||||||
|
String originalResponseContentType = exchange.getAttribute(ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR);
|
||||||
|
HttpHeaders httpHeaders = new HttpHeaders();
|
||||||
|
//explicitly add it in this way instead of 'httpHeaders.setContentType(originalResponseContentType)'
|
||||||
|
//this will prevent exception in case of using non-standard media types like "Content-Type: image"
|
||||||
|
httpHeaders.add(HttpHeaders.CONTENT_TYPE, originalResponseContentType);
|
||||||
|
ResponseAdapter responseAdapter = new ResponseAdapter(body, httpHeaders);
|
||||||
|
DefaultClientResponse clientResponse = new DefaultClientResponse(responseAdapter, ExchangeStrategies.withDefaults());
|
||||||
|
|
||||||
|
//TODO: flux or mono
|
||||||
|
Mono modifiedBody = clientResponse.bodyToMono(inClass)
|
||||||
|
.flatMap(originalBody -> {
|
||||||
|
// 合并微服务传递过来的结果,变成最终结果
|
||||||
|
ResultExecutor resultExecutor = ApiContext.getApiConfig().getResultExecutor();
|
||||||
|
String ret = resultExecutor.mergeResult(exchange, String.valueOf(originalBody));
|
||||||
|
return Mono.just(ret);
|
||||||
|
});
|
||||||
|
|
||||||
|
BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, outClass);
|
||||||
|
CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, exchange.getResponse().getHeaders());
|
||||||
|
return bodyInserter.insert(outputMessage, new BodyInserterContext())
|
||||||
|
.then(Mono.defer(() -> {
|
||||||
|
Flux<DataBuffer> messageBody = outputMessage.getBody();
|
||||||
|
HttpHeaders headers = getDelegate().getHeaders();
|
||||||
|
if (!headers.containsKey(HttpHeaders.TRANSFER_ENCODING)) {
|
||||||
|
messageBody = messageBody.doOnNext(data -> headers.setContentLength(data.readableByteCount()));
|
||||||
|
}
|
||||||
|
//TODO: use isStreamingMediaType?
|
||||||
|
return getDelegate().writeWith(messageBody);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<Void> writeAndFlushWith(Publisher<? extends Publisher<? extends DataBuffer>> body) {
|
||||||
|
return writeWith(Flux.from(body)
|
||||||
|
.flatMapSequential(p -> p));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return chain.filter(exchange.mutate().response(responseDecorator).build());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOrder() {
|
||||||
|
return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ResponseAdapter implements ClientHttpResponse {
|
||||||
|
|
||||||
|
private final Flux<DataBuffer> flux;
|
||||||
|
private final HttpHeaders headers;
|
||||||
|
|
||||||
|
public ResponseAdapter(Publisher<? extends DataBuffer> body, HttpHeaders headers) {
|
||||||
|
this.headers = headers;
|
||||||
|
if (body instanceof Flux) {
|
||||||
|
flux = (Flux) body;
|
||||||
|
} else {
|
||||||
|
flux = ((Mono) body).flux();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Flux<DataBuffer> getBody() {
|
||||||
|
return flux;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HttpHeaders getHeaders() {
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HttpStatus getStatusCode() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getRawStatusCode() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MultiValueMap<String, ResponseCookie> getCookies() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,56 @@
|
|||||||
|
package com.gitee.sop.gatewaycommon.filter;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
|
||||||
|
import org.springframework.cloud.gateway.filter.GlobalFilter;
|
||||||
|
import org.springframework.cloud.gateway.route.Route;
|
||||||
|
import org.springframework.core.Ordered;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
|
import org.springframework.web.util.UriComponentsBuilder;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
|
||||||
|
import static org.springframework.cloud.gateway.filter.LoadBalancerClientFilter.LOAD_BALANCER_CLIENT_FILTER_ORDER;
|
||||||
|
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
|
||||||
|
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR;
|
||||||
|
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.containsEncodedParts;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在LoadBalancerClientFilter后面处理,处理成我们想要的uri
|
||||||
|
*
|
||||||
|
* @author tanghc
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class LoadBalancerClientExtFilter implements GlobalFilter, Ordered {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOrder() {
|
||||||
|
return LOAD_BALANCER_CLIENT_FILTER_ORDER + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||||
|
URI url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
|
||||||
|
Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
|
||||||
|
URI routeUri = route.getUri();
|
||||||
|
|
||||||
|
URI requestUrl = url;
|
||||||
|
|
||||||
|
String uriStr = routeUri.toString();
|
||||||
|
String[] uriArr = uriStr.split("\\#");
|
||||||
|
if (uriArr.length == 2) {
|
||||||
|
String path = uriArr[1];
|
||||||
|
UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUri(url);
|
||||||
|
if (StringUtils.hasLength(path)) {
|
||||||
|
uriComponentsBuilder.path(path);
|
||||||
|
}
|
||||||
|
requestUrl = uriComponentsBuilder.build(true).toUri();
|
||||||
|
}
|
||||||
|
|
||||||
|
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl);
|
||||||
|
return chain.filter(exchange);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -1,49 +0,0 @@
|
|||||||
package com.gitee.sop.gatewaycommon.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 {
|
|
||||||
int responseStatusCode = requestContext.getResponseStatusCode();
|
|
||||||
InputStream responseDataStream = requestContext.getResponseDataStream();
|
|
||||||
ApiConfig apiConfig = ApiContext.getApiConfig();
|
|
||||||
ResultExecutor resultExecutor = apiConfig.getResultExecutor();
|
|
||||||
String serviceResult;
|
|
||||||
try {
|
|
||||||
serviceResult = IOUtils.toString(responseDataStream, SopConstants.CHARSET_UTF8);
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("业务方无数据返回", e);
|
|
||||||
serviceResult = SopConstants.EMPTY_JSON;
|
|
||||||
}
|
|
||||||
String finalResult = resultExecutor.mergeResult(responseStatusCode, serviceResult);
|
|
||||||
requestContext.setResponseBody(finalResult);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -1,41 +0,0 @@
|
|||||||
package com.gitee.sop.gatewaycommon.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -1,48 +0,0 @@
|
|||||||
package com.gitee.sop.gatewaycommon.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.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 {
|
|
||||||
HttpServletRequest request = requestContext.getRequest();
|
|
||||||
ApiConfig apiConfig = ApiContext.getApiConfig();
|
|
||||||
// 解析参数
|
|
||||||
ApiParam param = apiConfig.getParamParser().parse(request);
|
|
||||||
ApiContext.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,44 @@
|
|||||||
|
package com.gitee.sop.gatewaycommon.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.exception.ApiException;
|
||||||
|
import com.gitee.sop.gatewaycommon.param.ApiParam;
|
||||||
|
import com.gitee.sop.gatewaycommon.validate.Validator;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
|
||||||
|
import org.springframework.cloud.gateway.filter.GlobalFilter;
|
||||||
|
import org.springframework.core.Ordered;
|
||||||
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author tanghc
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class ValidateFilter implements GlobalFilter, Ordered {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||||
|
ApiConfig apiConfig = ApiContext.getApiConfig();
|
||||||
|
// 解析参数
|
||||||
|
ApiParam param = apiConfig.getParamBuilder().build(exchange);
|
||||||
|
exchange.getAttributes().put(SopConstants.CACHE_API_PARAM, param);
|
||||||
|
// 验证操作,这里有负责验证签名参数
|
||||||
|
Validator validator = apiConfig.getValidator();
|
||||||
|
try {
|
||||||
|
validator.validate(param);
|
||||||
|
} catch (ApiException e) {
|
||||||
|
log.error("验证失败,params:{}", param.toJSONString(), e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
return chain.filter(exchange);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOrder() {
|
||||||
|
// 最优先执行
|
||||||
|
return Ordered.HIGHEST_PRECEDENCE + 1000;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,155 @@
|
|||||||
|
package com.gitee.sop.gatewaycommon.handler;
|
||||||
|
|
||||||
|
import com.gitee.sop.gatewaycommon.bean.ApiContext;
|
||||||
|
import com.gitee.sop.gatewaycommon.result.GatewayResult;
|
||||||
|
import com.gitee.sop.gatewaycommon.result.ResultExecutor;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
|
||||||
|
import org.springframework.http.codec.HttpMessageReader;
|
||||||
|
import org.springframework.http.codec.HttpMessageWriter;
|
||||||
|
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.web.reactive.function.BodyInserters;
|
||||||
|
import org.springframework.web.reactive.function.server.RequestPredicates;
|
||||||
|
import org.springframework.web.reactive.function.server.RouterFunctions;
|
||||||
|
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||||
|
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||||
|
import org.springframework.web.reactive.result.view.ViewResolver;
|
||||||
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @classDesc: 统一异常处理, 参考{@link org.springframework.web.server.AbstractErrorWebExceptionHandler}修改
|
||||||
|
* @author: chenggang
|
||||||
|
* @createTime: 2018/10/30
|
||||||
|
*/
|
||||||
|
public class GatewayExceptionHandler implements ErrorWebExceptionHandler {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(GatewayExceptionHandler.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
|
||||||
|
ResultExecutor resultExecutor = ApiContext.getApiConfig().getResultExecutor();
|
||||||
|
GatewayResult errorResult = resultExecutor.buildErrorResult(exchange, ex);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误记录
|
||||||
|
*/
|
||||||
|
ServerHttpRequest request = exchange.getRequest();
|
||||||
|
log.error("[全局异常处理]异常请求路径:{},记录异常信息:{}", request.getPath(), ex.getMessage());
|
||||||
|
/**
|
||||||
|
* 参考AbstractErrorWebExceptionHandler
|
||||||
|
*/
|
||||||
|
if (exchange.getResponse().isCommitted()) {
|
||||||
|
return Mono.error(ex);
|
||||||
|
}
|
||||||
|
exceptionHandlerResult.set(errorResult);
|
||||||
|
ServerRequest newRequest = ServerRequest.create(exchange, this.messageReaders);
|
||||||
|
return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse).route(newRequest)
|
||||||
|
.switchIfEmpty(Mono.error(ex))
|
||||||
|
.flatMap((handler) -> handler.handle(newRequest))
|
||||||
|
.flatMap((response) -> write(exchange, response));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MessageReader
|
||||||
|
*/
|
||||||
|
private List<HttpMessageReader<?>> messageReaders = Collections.emptyList();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MessageWriter
|
||||||
|
*/
|
||||||
|
private List<HttpMessageWriter<?>> messageWriters = Collections.emptyList();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ViewResolvers
|
||||||
|
*/
|
||||||
|
private List<ViewResolver> viewResolvers = Collections.emptyList();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 存储处理异常后的信息
|
||||||
|
*/
|
||||||
|
private ThreadLocal<GatewayResult> exceptionHandlerResult = new ThreadLocal<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参考AbstractErrorWebExceptionHandler
|
||||||
|
*
|
||||||
|
* @param messageReaders
|
||||||
|
*/
|
||||||
|
public void setMessageReaders(List<HttpMessageReader<?>> messageReaders) {
|
||||||
|
Assert.notNull(messageReaders, "'messageReaders' must not be null");
|
||||||
|
this.messageReaders = messageReaders;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参考AbstractErrorWebExceptionHandler
|
||||||
|
*
|
||||||
|
* @param viewResolvers
|
||||||
|
*/
|
||||||
|
public void setViewResolvers(List<ViewResolver> viewResolvers) {
|
||||||
|
this.viewResolvers = viewResolvers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参考AbstractErrorWebExceptionHandler
|
||||||
|
*
|
||||||
|
* @param messageWriters
|
||||||
|
*/
|
||||||
|
public void setMessageWriters(List<HttpMessageWriter<?>> messageWriters) {
|
||||||
|
Assert.notNull(messageWriters, "'messageWriters' must not be null");
|
||||||
|
this.messageWriters = messageWriters;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参考DefaultErrorWebExceptionHandler
|
||||||
|
*
|
||||||
|
* @param request
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
protected Mono<ServerResponse> renderErrorResponse(ServerRequest request) {
|
||||||
|
GatewayResult result = exceptionHandlerResult.get();
|
||||||
|
return ServerResponse
|
||||||
|
.status(result.getHttpStatus())
|
||||||
|
.contentType(result.getContentType())
|
||||||
|
.body(BodyInserters.fromObject(result.getBody()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参考AbstractErrorWebExceptionHandler
|
||||||
|
*
|
||||||
|
* @param exchange
|
||||||
|
* @param response
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private Mono<? extends Void> write(ServerWebExchange exchange,
|
||||||
|
ServerResponse response) {
|
||||||
|
exchange.getResponse().getHeaders()
|
||||||
|
.setContentType(response.headers().getContentType());
|
||||||
|
return response.writeTo(exchange, new ResponseContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参考AbstractErrorWebExceptionHandler
|
||||||
|
*/
|
||||||
|
private class ResponseContext implements ServerResponse.Context {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<HttpMessageWriter<?>> messageWriters() {
|
||||||
|
return GatewayExceptionHandler.this.messageWriters;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ViewResolver> viewResolvers() {
|
||||||
|
return GatewayExceptionHandler.this.viewResolvers;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -1,28 +0,0 @@
|
|||||||
package com.gitee.sop.gatewaycommon.manager;
|
|
||||||
|
|
||||||
import org.springframework.data.redis.connection.Message;
|
|
||||||
import org.springframework.data.redis.connection.MessageListener;
|
|
||||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
|
||||||
import org.springframework.data.redis.serializer.RedisSerializer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author tanghc
|
|
||||||
*/
|
|
||||||
public class ApiMetaChangeListener implements MessageListener {
|
|
||||||
|
|
||||||
private ApiMetaManager apiMetaManager;
|
|
||||||
private StringRedisTemplate redisTemplate;
|
|
||||||
|
|
||||||
public ApiMetaChangeListener(ApiMetaManager apiMetaManager, StringRedisTemplate redisTemplate) {
|
|
||||||
this.apiMetaManager = apiMetaManager;
|
|
||||||
this.redisTemplate = redisTemplate;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onMessage(Message message, byte[] bytes) {
|
|
||||||
RedisSerializer<String> stringSerializer = redisTemplate.getStringSerializer();
|
|
||||||
String msg = stringSerializer.deserialize(message.getBody());
|
|
||||||
this.apiMetaManager.onChange(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -1,23 +0,0 @@
|
|||||||
package com.gitee.sop.gatewaycommon.manager;
|
|
||||||
|
|
||||||
import com.gitee.sop.gatewaycommon.bean.ServiceApiInfo;
|
|
||||||
import org.springframework.cloud.netflix.zuul.filters.Route;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author tanghc
|
|
||||||
*/
|
|
||||||
public interface ApiMetaContext {
|
|
||||||
/**
|
|
||||||
* 重新加载接口信息
|
|
||||||
* @param serviceId
|
|
||||||
* @param serviceApiInfo
|
|
||||||
*/
|
|
||||||
void reload(String serviceId, ServiceApiInfo serviceApiInfo);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取接口对应的route信息
|
|
||||||
* @param nameVersion
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
Route getRoute(String nameVersion);
|
|
||||||
}
|
|
@@ -1,62 +0,0 @@
|
|||||||
package com.gitee.sop.gatewaycommon.manager;
|
|
||||||
|
|
||||||
import com.gitee.sop.gatewaycommon.bean.ServiceApiInfo;
|
|
||||||
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.cloud.netflix.zuul.filters.Route;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author tanghc
|
|
||||||
*/
|
|
||||||
@Slf4j
|
|
||||||
public class DefaultApiMetaContext implements ApiMetaContext {
|
|
||||||
|
|
||||||
/** key:nameVersion */
|
|
||||||
private Map<String, Route> nameVersionServiceIdMap = new HashMap<>(128);
|
|
||||||
|
|
||||||
/** key: serviceId , value: md5 */
|
|
||||||
private Map<String, String> serviceIdMd5Map = new HashMap<>(16);
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void reload(String serviceId, ServiceApiInfo serviceApiInfo) {
|
|
||||||
String md5 = serviceIdMd5Map.get(serviceId);
|
|
||||||
if (md5 != null && md5.equals(serviceApiInfo.getMd5())) {
|
|
||||||
log.info("MD5相同,无需更改本地接口信息,appName:{}, md5:{}", serviceApiInfo.getAppName(), serviceApiInfo.getMd5());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
log.info("更新本地接口信息,appName:{}, md5:{}", serviceApiInfo.getAppName(), serviceApiInfo.getMd5());
|
|
||||||
// 移除原来的
|
|
||||||
Iterator<Map.Entry<String, Route>> iterator = nameVersionServiceIdMap.entrySet().iterator();
|
|
||||||
while (iterator.hasNext()) {
|
|
||||||
Map.Entry<String, Route> entry = iterator.next();
|
|
||||||
if (entry.getValue().getLocation().equals(serviceId)) {
|
|
||||||
iterator.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
List<ServiceApiInfo.ApiMeta> apis = serviceApiInfo.getApis();
|
|
||||||
for (ServiceApiInfo.ApiMeta apiMeta : apis) {
|
|
||||||
Route route = this.buildRoute(serviceId, apiMeta);
|
|
||||||
nameVersionServiceIdMap.put(apiMeta.fetchNameVersion(), route);
|
|
||||||
}
|
|
||||||
serviceIdMd5Map.put(serviceId, serviceApiInfo.getMd5());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Route getRoute(String nameVersion) {
|
|
||||||
Route route = nameVersionServiceIdMap.get(nameVersion);
|
|
||||||
if (route == null) {
|
|
||||||
throw ErrorEnum.ISV_INVALID_METHOD.getErrorMeta().getException();
|
|
||||||
}
|
|
||||||
return route;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Route buildRoute(String serviceId, ServiceApiInfo.ApiMeta apiMeta) {
|
|
||||||
return new Route(apiMeta.getName(), apiMeta.getPath(), serviceId, null, false, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -1,51 +0,0 @@
|
|||||||
package com.gitee.sop.gatewaycommon.manager;
|
|
||||||
|
|
||||||
import com.alibaba.fastjson.JSON;
|
|
||||||
import com.gitee.sop.gatewaycommon.bean.ServiceApiInfo;
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 保存在redis中,结构为HSET。格式如下:
|
|
||||||
* <pre>
|
|
||||||
* com.gitee.sop.api
|
|
||||||
* <serviceId>:{ md5:"xxx", apis:[{name:"", version:""}] }
|
|
||||||
* </pre>
|
|
||||||
* @author tanghc
|
|
||||||
*/
|
|
||||||
@Getter
|
|
||||||
@Slf4j
|
|
||||||
public class DefaultApiMetaManager implements ApiMetaManager {
|
|
||||||
|
|
||||||
private StringRedisTemplate redisTemplate;
|
|
||||||
private ApiMetaContext apiMetaContext;
|
|
||||||
|
|
||||||
public DefaultApiMetaManager(StringRedisTemplate redisTemplate, ApiMetaContext apiMetaContext) {
|
|
||||||
this.redisTemplate = redisTemplate;
|
|
||||||
this.apiMetaContext = apiMetaContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void refresh() {
|
|
||||||
log.info("刷新本地接口信息");
|
|
||||||
Map<Object, Object> entries = redisTemplate.opsForHash().entries(API_STORE_KEY);
|
|
||||||
for (Map.Entry<Object, Object> entry : entries.entrySet()) {
|
|
||||||
log.info("更新微服务接口,appName:{}", entry.getKey());
|
|
||||||
String serviceId = entry.getKey().toString();
|
|
||||||
String serviceApiInfoJson = entry.getValue().toString();
|
|
||||||
ServiceApiInfo serviceApiInfo = JSON.parseObject(serviceApiInfoJson, ServiceApiInfo.class);
|
|
||||||
apiMetaContext.reload(serviceId, serviceApiInfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onChange(String serviceApiInfoJson) {
|
|
||||||
ServiceApiInfo serviceApiInfo = JSON.parseObject(serviceApiInfoJson, ServiceApiInfo.class);
|
|
||||||
log.info("Redis订阅推送接口信息,appName:{}, md5:{}", serviceApiInfo.getAppName(), serviceApiInfo.getMd5());
|
|
||||||
this.apiMetaContext.reload(serviceApiInfo.getAppName(), serviceApiInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -0,0 +1,168 @@
|
|||||||
|
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) {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -1,45 +0,0 @@
|
|||||||
package com.gitee.sop.gatewaycommon.manager;
|
|
||||||
|
|
||||||
import com.gitee.sop.gatewaycommon.bean.ApiContext;
|
|
||||||
import com.gitee.sop.gatewaycommon.param.ApiParam;
|
|
||||||
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 ApiMetaContext apiMetaContext;
|
|
||||||
|
|
||||||
public SopRouteLocator(ApiMetaContext apiMetaContext) {
|
|
||||||
this.apiMetaContext = apiMetaContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<String> getIgnoredPaths() {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<Route> getRoutes() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Route getMatchingRoute(String path) {
|
|
||||||
ApiParam param = ApiContext.getApiParam();
|
|
||||||
String nameVersion = param.fetchNameVersion();
|
|
||||||
return apiMetaContext.getRoute(nameVersion);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getOrder() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -4,6 +4,8 @@ import com.gitee.sop.gatewaycommon.bean.ApiContext;
|
|||||||
import com.gitee.sop.gatewaycommon.exception.ApiException;
|
import com.gitee.sop.gatewaycommon.exception.ApiException;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 错误对象
|
* 错误对象
|
||||||
*
|
*
|
||||||
@@ -12,6 +14,8 @@ import lombok.Getter;
|
|||||||
@Getter
|
@Getter
|
||||||
public class ErrorMeta {
|
public class ErrorMeta {
|
||||||
|
|
||||||
|
private static final Locale ZH_CN = Locale.SIMPLIFIED_CHINESE;
|
||||||
|
|
||||||
private String modulePrefix;
|
private String modulePrefix;
|
||||||
private String code;
|
private String code;
|
||||||
private String subCode;
|
private String subCode;
|
||||||
@@ -23,7 +27,7 @@ public class ErrorMeta {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Error getError() {
|
public Error getError() {
|
||||||
return ErrorFactory.getError(this, ApiContext.getLocale());
|
return ErrorFactory.getError(this, ZH_CN);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -33,14 +37,18 @@ public class ErrorMeta {
|
|||||||
* @return 返回exception
|
* @return 返回exception
|
||||||
*/
|
*/
|
||||||
public ApiException getException(Object... params) {
|
public ApiException getException(Object... params) {
|
||||||
|
Locale locale = ZH_CN;
|
||||||
if (params != null && params.length == 1) {
|
if (params != null && params.length == 1) {
|
||||||
Object param = params[0];
|
Object param = params[0];
|
||||||
if (param instanceof Throwable) {
|
if (param instanceof Throwable) {
|
||||||
Error error = ErrorFactory.getError(this, ApiContext.getLocale());
|
Error error = ErrorFactory.getError(this, ZH_CN);
|
||||||
return new ApiException((Throwable) param, error);
|
return new ApiException((Throwable) param, error);
|
||||||
}
|
}
|
||||||
|
if (param instanceof Locale) {
|
||||||
|
locale = (Locale) param;
|
||||||
}
|
}
|
||||||
Error error = ErrorFactory.getError(this, ApiContext.getLocale(), params);
|
}
|
||||||
|
Error error = ErrorFactory.getError(this, locale, params);
|
||||||
return new ApiException(error);
|
return new ApiException(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -2,8 +2,9 @@ package com.gitee.sop.gatewaycommon.param;
|
|||||||
|
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
import com.gitee.sop.gatewaycommon.bean.SopConstants;
|
import com.gitee.sop.gatewaycommon.bean.SopConstants;
|
||||||
import com.netflix.zuul.context.RequestContext;
|
//import com.netflix.zuul.context.RequestContext;
|
||||||
import org.apache.commons.lang.StringUtils;
|
//import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -14,6 +15,10 @@ import java.util.Map;
|
|||||||
* @author tanghc
|
* @author tanghc
|
||||||
*/
|
*/
|
||||||
public class ApiParam extends JSONObject implements Param {
|
public class ApiParam extends JSONObject implements Param {
|
||||||
|
|
||||||
|
public ApiParam() {
|
||||||
|
}
|
||||||
|
|
||||||
public ApiParam(Map<String, Object> map) {
|
public ApiParam(Map<String, Object> map) {
|
||||||
super(map);
|
super(map);
|
||||||
}
|
}
|
||||||
@@ -36,7 +41,8 @@ public class ApiParam extends JSONObject implements Param {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public HttpServletRequest fetchRequest() {
|
public HttpServletRequest fetchRequest() {
|
||||||
return RequestContext.getCurrentContext().getRequest();
|
// return RequestContext.getCurrentContext().getRequest();
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -0,0 +1,23 @@
|
|||||||
|
package com.gitee.sop.gatewaycommon.param;
|
||||||
|
|
||||||
|
import com.gitee.sop.gatewaycommon.bean.SopConstants;
|
||||||
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author tanghc
|
||||||
|
*/
|
||||||
|
public class ApiParamBuilder implements ParamBuilder {
|
||||||
|
@Override
|
||||||
|
public ApiParam build(ServerWebExchange exchange) {
|
||||||
|
Map<String, String> params = exchange.getAttribute(SopConstants.CACHE_REQUEST_BODY_FOR_MAP);
|
||||||
|
ApiParam apiParam = new ApiParam();
|
||||||
|
for (Map.Entry<String, String> entry : params.entrySet()) {
|
||||||
|
apiParam.put(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
return apiParam;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@@ -1,104 +0,0 @@
|
|||||||
package com.gitee.sop.gatewaycommon.param;
|
|
||||||
|
|
||||||
import com.alibaba.fastjson.JSON;
|
|
||||||
import com.gitee.sop.gatewaycommon.bean.ApiContext;
|
|
||||||
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
|
|
||||||
import com.gitee.sop.gatewaycommon.util.RequestUtil;
|
|
||||||
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 ApiParamParser implements ParamParser {
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ApiParam parse(HttpServletRequest request) {
|
|
||||||
try {
|
|
||||||
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 (RequestUtil.isGetRequest(request)) {
|
|
||||||
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) {
|
|
||||||
// 保存上传文件
|
|
||||||
ApiContext.setUploadContext(new ApiUploadContext(finalMap));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return RequestUtil.convertRequestParamsToMap(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -0,0 +1,16 @@
|
|||||||
|
package com.gitee.sop.gatewaycommon.param;
|
||||||
|
|
||||||
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author tanghc
|
||||||
|
*/
|
||||||
|
public interface ParamBuilder {
|
||||||
|
/**
|
||||||
|
* 从request提取参数
|
||||||
|
* @param exchange
|
||||||
|
* @return 返回ApiParam
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
ApiParam build(ServerWebExchange exchange);
|
||||||
|
}
|
@@ -1,19 +0,0 @@
|
|||||||
package com.gitee.sop.gatewaycommon.param;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 负责解析参数
|
|
||||||
* @author tanghc
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public interface ParamParser {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 从request提取参数
|
|
||||||
* @param request
|
|
||||||
* @return 返回ApiParam
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
ApiParam parse(HttpServletRequest request);
|
|
||||||
}
|
|
@@ -2,16 +2,23 @@ package com.gitee.sop.gatewaycommon.result;
|
|||||||
|
|
||||||
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSON;
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
import com.gitee.sop.gatewaycommon.bean.ApiContext;
|
import com.gitee.sop.gatewaycommon.bean.SopConstants;
|
||||||
import com.gitee.sop.gatewaycommon.exception.ApiException;
|
import com.gitee.sop.gatewaycommon.exception.ApiException;
|
||||||
import com.gitee.sop.gatewaycommon.message.Error;
|
import com.gitee.sop.gatewaycommon.message.Error;
|
||||||
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
|
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
|
||||||
import com.gitee.sop.gatewaycommon.message.ErrorMeta;
|
import com.gitee.sop.gatewaycommon.message.ErrorMeta;
|
||||||
import com.gitee.sop.gatewaycommon.param.ApiParam;
|
|
||||||
import com.gitee.sop.gatewaycommon.param.ParamNames;
|
import com.gitee.sop.gatewaycommon.param.ParamNames;
|
||||||
import com.netflix.zuul.exception.ZuulException;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.cloud.gateway.support.NotFoundException;
|
||||||
import org.springframework.http.HttpStatus;
|
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
|
* @author tanghc
|
||||||
@@ -32,32 +39,46 @@ public class ApiResultExecutor implements ResultExecutor {
|
|||||||
public static final String DATA_SUFFIX = "_response";
|
public static final String DATA_SUFFIX = "_response";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String mergeResult(int responseStatus, String responseData) {
|
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) {
|
if (responseStatus == HttpStatus.OK.value() || responseStatus == BIZ_ERROR_STATUS) {
|
||||||
return mergeSuccess(responseStatus, responseData);
|
return mergeSuccess(exchange, responseStatus, responseData);
|
||||||
} else {
|
} else {
|
||||||
// 微服务端有可能返回500错误
|
// 微服务端有可能返回500错误
|
||||||
// {"path":"/book/getBook3","error":"Internal Server Error","message":"id不能为空","timestamp":"2019-02-13T07:41:00.495+0000","status":500}
|
// {"path":"/book/getBook3","error":"Internal Server Error","message":"id不能为空","timestamp":"2019-02-13T07:41:00.495+0000","status":500}
|
||||||
return mergeError(responseData);
|
return mergeError(exchange, responseData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String mergeError(Throwable throwable) {
|
public GatewayResult buildErrorResult(ServerWebExchange exchange, Throwable ex) {
|
||||||
Error error = null;
|
Error error = null;
|
||||||
if (throwable instanceof ZuulException) {
|
if (ex instanceof ApiException) {
|
||||||
ZuulException ex = (ZuulException) throwable;
|
ApiException apiException = (ApiException) ex;
|
||||||
Throwable cause = ex.getCause();
|
|
||||||
if (cause instanceof ApiException) {
|
|
||||||
ApiException apiException = (ApiException) cause;
|
|
||||||
error = apiException.getError();
|
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) {
|
if (error == null) {
|
||||||
error = ErrorEnum.AOP_UNKNOW_ERROR.getErrorMeta().getError();
|
error = ErrorEnum.AOP_UNKNOW_ERROR.getErrorMeta().getError();
|
||||||
}
|
}
|
||||||
|
|
||||||
JSONObject jsonObject = (JSONObject) JSON.toJSON(error);
|
JSONObject jsonObject = (JSONObject) JSON.toJSON(error);
|
||||||
return this.merge(jsonObject);
|
String body = this.merge(exchange, jsonObject);
|
||||||
|
|
||||||
|
return new GatewayResult(HttpStatus.OK, MediaType.APPLICATION_JSON_UTF8, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -76,7 +97,7 @@ public class ApiResultExecutor implements ResultExecutor {
|
|||||||
"sign": "ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE"
|
"sign": "ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE"
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
public String mergeSuccess(int responseStatus, String serviceResult) {
|
public String mergeSuccess(ServerWebExchange exchange, int responseStatus, String serviceResult) {
|
||||||
JSONObject jsonObjectService;
|
JSONObject jsonObjectService;
|
||||||
// 如果是业务出错
|
// 如果是业务出错
|
||||||
if (responseStatus == BIZ_ERROR_STATUS) {
|
if (responseStatus == BIZ_ERROR_STATUS) {
|
||||||
@@ -89,7 +110,7 @@ public class ApiResultExecutor implements ResultExecutor {
|
|||||||
jsonObjectService.put(GATEWAY_CODE_NAME, SUCCESS_META.getCode());
|
jsonObjectService.put(GATEWAY_CODE_NAME, SUCCESS_META.getCode());
|
||||||
jsonObjectService.put(GATEWAY_MSG_NAME, SUCCESS_META.getError().getMsg());
|
jsonObjectService.put(GATEWAY_MSG_NAME, SUCCESS_META.getError().getMsg());
|
||||||
}
|
}
|
||||||
return this.merge(jsonObjectService);
|
return this.merge(exchange, jsonObjectService);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -104,20 +125,20 @@ public class ApiResultExecutor implements ResultExecutor {
|
|||||||
"sign": "ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE"
|
"sign": "ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE"
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
public String mergeError(String serviceResult) {
|
public String mergeError(ServerWebExchange exchange, String serviceResult) {
|
||||||
JSONObject jsonObjectService = new JSONObject();
|
JSONObject jsonObjectService = new JSONObject();
|
||||||
jsonObjectService.put(GATEWAY_CODE_NAME, ISP_UNKNOW_ERROR_META.getCode());
|
jsonObjectService.put(GATEWAY_CODE_NAME, ISP_UNKNOW_ERROR_META.getCode());
|
||||||
jsonObjectService.put(GATEWAY_MSG_NAME, ISP_UNKNOW_ERROR_META.getError().getMsg());
|
jsonObjectService.put(GATEWAY_MSG_NAME, ISP_UNKNOW_ERROR_META.getError().getMsg());
|
||||||
return this.merge(jsonObjectService);
|
return this.merge(exchange, jsonObjectService);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String merge(JSONObject jsonObjectService) {
|
private String merge(ServerWebExchange exchange, JSONObject jsonObjectService) {
|
||||||
JSONObject ret = new JSONObject();
|
JSONObject ret = new JSONObject();
|
||||||
ApiParam apiParam = ApiContext.getApiParam();
|
|
||||||
// 点换成下划线
|
// 点换成下划线
|
||||||
String apiName = apiParam.fetchName().replace(DOT, UNDERLINE);
|
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(apiName + DATA_SUFFIX, jsonObjectService);
|
||||||
ret.put(ParamNames.SIGN_NAME, apiParam.fetchSign());
|
ret.put(ParamNames.SIGN_NAME, params.getOrDefault(ParamNames.SIGN_NAME, ""));
|
||||||
return ret.toJSONString();
|
return ret.toJSONString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -0,0 +1,21 @@
|
|||||||
|
package com.gitee.sop.gatewaycommon.result;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author tanghc
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class GatewayResult {
|
||||||
|
private HttpStatus httpStatus;
|
||||||
|
private MediaType contentType;
|
||||||
|
private String body;
|
||||||
|
|
||||||
|
public GatewayResult(HttpStatus httpStatus, MediaType contentType, String body) {
|
||||||
|
this.httpStatus = httpStatus;
|
||||||
|
this.contentType = contentType;
|
||||||
|
this.body = body;
|
||||||
|
}
|
||||||
|
}
|
@@ -1,16 +0,0 @@
|
|||||||
package com.gitee.sop.gatewaycommon.result;
|
|
||||||
|
|
||||||
import com.alibaba.fastjson.JSON;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 序列化json
|
|
||||||
* @author tanghc
|
|
||||||
*/
|
|
||||||
public class JsonResultSerializer implements ResultSerializer {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String serialize(Object obj) {
|
|
||||||
return JSON.toJSONString(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -1,27 +0,0 @@
|
|||||||
package com.gitee.sop.gatewaycommon.result;
|
|
||||||
|
|
||||||
import com.gitee.sop.gatewaycommon.message.Error;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author tanghc
|
|
||||||
*/
|
|
||||||
public interface ResultBuilder {
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 构建网关错误返回结果
|
|
||||||
*
|
|
||||||
* @param throwable 异常
|
|
||||||
* @return 返回最终结果
|
|
||||||
*/
|
|
||||||
Result buildGatewayError(Throwable throwable);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 构建网关错误返回结果
|
|
||||||
*
|
|
||||||
* @param error error
|
|
||||||
* @return 返回最终结果
|
|
||||||
*/
|
|
||||||
Result buildGatewayError(Error error);
|
|
||||||
|
|
||||||
}
|
|
@@ -1,21 +1,25 @@
|
|||||||
package com.gitee.sop.gatewaycommon.result;
|
package com.gitee.sop.gatewaycommon.result;
|
||||||
|
|
||||||
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* 对返回结果进行处理
|
||||||
* @author tanghc
|
* @author tanghc
|
||||||
*/
|
*/
|
||||||
public interface ResultExecutor {
|
public interface ResultExecutor {
|
||||||
/**
|
/**
|
||||||
* 合并结果
|
* 合并结果
|
||||||
* @param responseStatus
|
* @param exchange
|
||||||
* @param responseData
|
* @param responseData
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
String mergeResult(int responseStatus, String responseData);
|
String mergeResult(ServerWebExchange exchange, String responseData);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 合并错误结果
|
* 合并错误结果
|
||||||
* @param throwable
|
* @param exchange
|
||||||
|
* @param ex
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
String mergeError(Throwable throwable);
|
GatewayResult buildErrorResult(ServerWebExchange exchange, Throwable ex);
|
||||||
}
|
}
|
||||||
|
@@ -1,16 +0,0 @@
|
|||||||
package com.gitee.sop.gatewaycommon.result;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 对象序列化
|
|
||||||
* @author tanghc
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public interface ResultSerializer {
|
|
||||||
/**
|
|
||||||
* 序列化
|
|
||||||
*
|
|
||||||
* @param obj
|
|
||||||
* @return 返回序列化后的结果
|
|
||||||
*/
|
|
||||||
String serialize(Object obj);
|
|
||||||
}
|
|
@@ -1,16 +0,0 @@
|
|||||||
package com.gitee.sop.gatewaycommon.result;
|
|
||||||
|
|
||||||
import com.gitee.sop.gatewaycommon.util.XmlUtil;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 序列化成xml
|
|
||||||
* @author tanghc
|
|
||||||
*/
|
|
||||||
public class XmlResultSerializer implements ResultSerializer {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String serialize(Object obj) {
|
|
||||||
return XmlUtil.serialize(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -0,0 +1,68 @@
|
|||||||
|
package com.gitee.sop.gatewaycommon.route;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
|
||||||
|
import org.springframework.cloud.gateway.route.RouteDefinition;
|
||||||
|
import org.springframework.cloud.gateway.route.RouteDefinitionRepository;
|
||||||
|
import org.springframework.cloud.gateway.support.NotFoundException;
|
||||||
|
import org.springframework.context.ApplicationEventPublisher;
|
||||||
|
import org.springframework.context.ApplicationEventPublisherAware;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 动态更新路由
|
||||||
|
* @author thc
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class DynamicRouteServiceManager implements ApplicationEventPublisherAware {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RouteDefinitionRepository routeDefinitionRepository;
|
||||||
|
|
||||||
|
private ApplicationEventPublisher publisher;
|
||||||
|
|
||||||
|
/** 根据ID获取路由 */
|
||||||
|
public RouteDefinition get(String id) {
|
||||||
|
return routeDefinitionRepository.getRouteDefinitions()
|
||||||
|
.filter(routeDefinition -> {
|
||||||
|
return routeDefinition.getId().equals(id);
|
||||||
|
}).blockFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 增加路由 */
|
||||||
|
public String add(RouteDefinition definition) {
|
||||||
|
routeDefinitionRepository.save(Mono.just(definition)).subscribe();
|
||||||
|
this.publisher.publishEvent(new RefreshRoutesEvent(this));
|
||||||
|
return "success";
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 更新路由 */
|
||||||
|
public String update(RouteDefinition definition) {
|
||||||
|
try {
|
||||||
|
this.routeDefinitionRepository.delete(Mono.just(definition.getId()));
|
||||||
|
} catch (Exception e) {
|
||||||
|
return "update fail,not find route routeId: " + definition.getId();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
routeDefinitionRepository.save(Mono.just(definition)).subscribe();
|
||||||
|
this.publisher.publishEvent(new RefreshRoutesEvent(this));
|
||||||
|
return "success";
|
||||||
|
} catch (Exception e) {
|
||||||
|
return "update route fail";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除路由 */
|
||||||
|
public Mono<ResponseEntity<Object>> delete(String id) {
|
||||||
|
return 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()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
|
||||||
|
this.publisher = applicationEventPublisher;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,31 @@
|
|||||||
|
package com.gitee.sop.gatewaycommon.route;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.ToString;
|
||||||
|
import org.springframework.util.LinkedMultiValueMap;
|
||||||
|
import org.springframework.util.MultiValueMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author tanghc
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@ToString
|
||||||
|
public class GatewayContext {
|
||||||
|
|
||||||
|
public static final String CACHE_GATEWAY_CONTEXT = "cacheGatewayContext";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* cache json body
|
||||||
|
*/
|
||||||
|
private String cacheBody;
|
||||||
|
/**
|
||||||
|
* cache formdata
|
||||||
|
*/
|
||||||
|
private MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
|
||||||
|
/**
|
||||||
|
* cache reqeust path
|
||||||
|
*/
|
||||||
|
private String path;
|
||||||
|
}
|
@@ -0,0 +1,17 @@
|
|||||||
|
package com.gitee.sop.gatewaycommon.route;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author tanghc
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class GatewayFilterDefinition {
|
||||||
|
//Filter Name */
|
||||||
|
private String name;
|
||||||
|
//对应的路由规则 */
|
||||||
|
private Map<String, String> args = new LinkedHashMap<>();
|
||||||
|
}
|
@@ -0,0 +1,17 @@
|
|||||||
|
package com.gitee.sop.gatewaycommon.route;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author tanghc
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class GatewayPredicateDefinition {
|
||||||
|
/** 断言对应的Name */
|
||||||
|
private String name;
|
||||||
|
/** 配置的断言规则 */
|
||||||
|
private Map<String, String> args = new LinkedHashMap<>();
|
||||||
|
}
|
@@ -0,0 +1,23 @@
|
|||||||
|
package com.gitee.sop.gatewaycommon.route;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author tanghc
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class GatewayRouteDefinition {
|
||||||
|
/** 路由的Id */
|
||||||
|
private String id;
|
||||||
|
/** 路由断言集合配置 */
|
||||||
|
private List<GatewayPredicateDefinition> predicates = new ArrayList<>();
|
||||||
|
/** 路由过滤器集合配置 */
|
||||||
|
private List<GatewayFilterDefinition> filters = new ArrayList<>();
|
||||||
|
/** 路由规则转发的目标uri */
|
||||||
|
private String uri;
|
||||||
|
/** 路由执行的顺序 */
|
||||||
|
private int order = 0;
|
||||||
|
}
|
@@ -0,0 +1,91 @@
|
|||||||
|
package com.gitee.sop.gatewaycommon.route;
|
||||||
|
|
||||||
|
import com.gitee.sop.gatewaycommon.bean.SopConstants;
|
||||||
|
import com.gitee.sop.gatewaycommon.param.ParamNames;
|
||||||
|
import com.gitee.sop.gatewaycommon.util.RequestUtil;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotEmpty;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author tanghc
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class NameVersionRoutePredicateFactory extends AbstractRoutePredicateFactory<NameVersionRoutePredicateFactory.Config> {
|
||||||
|
|
||||||
|
public static final String PARAM_KEY = "param";
|
||||||
|
public static final String REGEXP_KEY = "regexp";
|
||||||
|
|
||||||
|
private static final String CACHE_REQUEST_BODY_OBJECT_KEY = SopConstants.CACHE_REQUEST_BODY_OBJECT_KEY;
|
||||||
|
public static final String CACHE_REQUEST_BODY_FOR_MAP = SopConstants.CACHE_REQUEST_BODY_FOR_MAP;
|
||||||
|
|
||||||
|
|
||||||
|
public NameVersionRoutePredicateFactory() {
|
||||||
|
super(Config.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> shortcutFieldOrder() {
|
||||||
|
return Arrays.asList(PARAM_KEY, REGEXP_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* config.param为nameVersion
|
||||||
|
*
|
||||||
|
* @param config
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Predicate<ServerWebExchange> apply(Config config) {
|
||||||
|
|
||||||
|
return exchange -> {
|
||||||
|
String cachedBody = exchange.getAttribute(CACHE_REQUEST_BODY_OBJECT_KEY);
|
||||||
|
if (cachedBody == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Map<String, String> params = exchange.getAttribute(CACHE_REQUEST_BODY_FOR_MAP);
|
||||||
|
if (params == null) {
|
||||||
|
params = RequestUtil.parseQueryToMap(cachedBody);
|
||||||
|
exchange.getAttributes().put(CACHE_REQUEST_BODY_FOR_MAP, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
String nameVersion = config.param;
|
||||||
|
String name = params.getOrDefault(ParamNames.API_NAME, "");
|
||||||
|
String version = params.getOrDefault(ParamNames.VERSION_NAME, "");
|
||||||
|
return (name + version).equals(nameVersion);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Validated
|
||||||
|
public static class Config {
|
||||||
|
@NotEmpty
|
||||||
|
private String param;
|
||||||
|
|
||||||
|
private String regexp;
|
||||||
|
|
||||||
|
public String getParam() {
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Config setParam(String param) {
|
||||||
|
this.param = param;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRegexp() {
|
||||||
|
return regexp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Config setRegexp(String regexp) {
|
||||||
|
this.regexp = regexp;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,25 @@
|
|||||||
|
package com.gitee.sop.gatewaycommon.route;
|
||||||
|
|
||||||
|
import org.springframework.cloud.gateway.handler.predicate.ReadBodyPredicateFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取form表单插件,使用方式:
|
||||||
|
* @Bean
|
||||||
|
* ReadBodyRoutePredicateFactory readBodyRoutePredicateFactory() {
|
||||||
|
* return new ReadBodyRoutePredicateFactory();
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @author tanghc
|
||||||
|
*/
|
||||||
|
public class ReadBodyRoutePredicateFactory extends ReadBodyPredicateFactory {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Config newConfig() {
|
||||||
|
Config config = super.newConfig();
|
||||||
|
config.setInClass(String.class);
|
||||||
|
config.setPredicate(body -> {
|
||||||
|
return body != null;
|
||||||
|
});
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,12 @@
|
|||||||
|
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,6 +1,5 @@
|
|||||||
package com.gitee.sop.gatewaycommon.session;
|
package com.gitee.sop.gatewaycommon.session;
|
||||||
|
|
||||||
import com.gitee.sop.gatewaycommon.bean.ApiContext;
|
|
||||||
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
|
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
|
||||||
import com.google.common.cache.CacheBuilder;
|
import com.google.common.cache.CacheBuilder;
|
||||||
import com.google.common.cache.CacheLoader;
|
import com.google.common.cache.CacheLoader;
|
||||||
@@ -16,7 +15,6 @@ import java.util.concurrent.TimeUnit;
|
|||||||
* session管理,默认存放的是{@link ApiHttpSession}。采用谷歌guava缓存实现。
|
* session管理,默认存放的是{@link ApiHttpSession}。采用谷歌guava缓存实现。
|
||||||
*
|
*
|
||||||
* @author tanghc
|
* @author tanghc
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public class ApiSessionManager implements SessionManager {
|
public class ApiSessionManager implements SessionManager {
|
||||||
private static Logger logger = LoggerFactory.getLogger(ApiSessionManager.class);
|
private static Logger logger = LoggerFactory.getLogger(ApiSessionManager.class);
|
||||||
@@ -50,7 +48,7 @@ public class ApiSessionManager implements SessionManager {
|
|||||||
* @return 返回session
|
* @return 返回session
|
||||||
*/
|
*/
|
||||||
protected HttpSession createSession(String sessionId) {
|
protected HttpSession createSession(String sessionId) {
|
||||||
ServletContext servletContext = getServletContext();
|
ServletContext servletContext = null;
|
||||||
HttpSession session = this.newSession(sessionId, servletContext);
|
HttpSession session = this.newSession(sessionId, servletContext);
|
||||||
session.setMaxInactiveInterval(getSessionTimeout());
|
session.setMaxInactiveInterval(getSessionTimeout());
|
||||||
this.cache.put(session.getId(), session);
|
this.cache.put(session.getId(), session);
|
||||||
@@ -68,10 +66,6 @@ public class ApiSessionManager implements SessionManager {
|
|||||||
return new ApiHttpSession(servletContext, sessionId);
|
return new ApiHttpSession(servletContext, sessionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ServletContext getServletContext() {
|
|
||||||
return ApiContext.getServletContext();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected LoadingCache<String, HttpSession> buildCache() {
|
protected LoadingCache<String, HttpSession> buildCache() {
|
||||||
return CacheBuilder.newBuilder().expireAfterAccess(getSessionTimeout(), TimeUnit.MINUTES)
|
return CacheBuilder.newBuilder().expireAfterAccess(getSessionTimeout(), TimeUnit.MINUTES)
|
||||||
.build(new CacheLoader<String, HttpSession>() {
|
.build(new CacheLoader<String, HttpSession>() {
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
package com.gitee.sop.gatewaycommon.session;
|
package com.gitee.sop.gatewaycommon.session;
|
||||||
|
|
||||||
import com.gitee.sop.gatewaycommon.bean.ApiContext;
|
|
||||||
import org.springframework.data.redis.core.RedisTemplate;
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
@@ -12,15 +11,18 @@ import java.util.UUID;
|
|||||||
* SessionManager的redis实现,使用redis管理session
|
* SessionManager的redis实现,使用redis管理session
|
||||||
*
|
*
|
||||||
* @author tanghc
|
* @author tanghc
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public class RedisSessionManager implements SessionManager {
|
public class RedisSessionManager implements SessionManager {
|
||||||
|
|
||||||
private ApiRedisTemplate redisTemplate;
|
private ApiRedisTemplate redisTemplate;
|
||||||
|
|
||||||
/** 过期时间,30分钟 */
|
/**
|
||||||
|
* 过期时间,30分钟
|
||||||
|
*/
|
||||||
private int sessionTimeout = 30;
|
private int sessionTimeout = 30;
|
||||||
/** 存入redis中key的前缀 */
|
/**
|
||||||
|
* 存入redis中key的前缀
|
||||||
|
*/
|
||||||
private String keyPrefix = "session:";
|
private String keyPrefix = "session:";
|
||||||
|
|
||||||
public RedisSessionManager(@SuppressWarnings("rawtypes") RedisTemplate redisTemplate) {
|
public RedisSessionManager(@SuppressWarnings("rawtypes") RedisTemplate redisTemplate) {
|
||||||
@@ -45,6 +47,7 @@ public class RedisSessionManager implements SessionManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 构建sessionId
|
* 构建sessionId
|
||||||
|
*
|
||||||
* @param id
|
* @param id
|
||||||
* @return 返回sessionid
|
* @return 返回sessionid
|
||||||
*/
|
*/
|
||||||
@@ -62,7 +65,7 @@ public class RedisSessionManager implements SessionManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ServletContext getServletContext() {
|
public ServletContext getServletContext() {
|
||||||
return ApiContext.getServletContext();
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getSessionTimeout() {
|
public int getSessionTimeout() {
|
||||||
@@ -71,6 +74,7 @@ public class RedisSessionManager implements SessionManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置session过期时间,单位分钟
|
* 设置session过期时间,单位分钟
|
||||||
|
*
|
||||||
* @param sessionTimeout
|
* @param sessionTimeout
|
||||||
*/
|
*/
|
||||||
public void setSessionTimeout(int sessionTimeout) {
|
public void setSessionTimeout(int sessionTimeout) {
|
||||||
@@ -91,6 +95,7 @@ public class RedisSessionManager implements SessionManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置存入redis中key的前缀,默认为"session:"
|
* 设置存入redis中key的前缀,默认为"session:"
|
||||||
|
*
|
||||||
* @param keyPrefix
|
* @param keyPrefix
|
||||||
*/
|
*/
|
||||||
public void setKeyPrefix(String keyPrefix) {
|
public void setKeyPrefix(String keyPrefix) {
|
||||||
|
@@ -1,137 +1,46 @@
|
|||||||
package com.gitee.sop.gatewaycommon.util;
|
package com.gitee.sop.gatewaycommon.util;
|
||||||
|
|
||||||
import com.alibaba.fastjson.JSON;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
|
|
||||||
import org.apache.commons.io.IOUtils;
|
|
||||||
import org.apache.commons.lang.StringUtils;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.io.IOException;
|
import java.net.URLDecoder;
|
||||||
import java.net.InetAddress;
|
|
||||||
import java.net.UnknownHostException;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author tanghc
|
* @author tanghc
|
||||||
*/
|
*/
|
||||||
public class RequestUtil {
|
public class RequestUtil {
|
||||||
|
|
||||||
private static final String CONTENT_TYPE_URLENCODED = "application/x-www-form-urlencoded";
|
|
||||||
private static final String CONTENT_TYPE_JSON = "application/json";
|
|
||||||
private static final String CONTENT_TYPE_TEXT = "text/plain";
|
|
||||||
|
|
||||||
private static final String UTF8 = "UTF-8";
|
private static final String UTF8 = "UTF-8";
|
||||||
private static final String GET = "get";
|
|
||||||
|
|
||||||
private static final String UNKOWN = "unknown";
|
|
||||||
private static final String LOCAL_IP = "127.0.0.1";
|
|
||||||
private static final int IP_LEN = 15;
|
|
||||||
|
|
||||||
public static String getText(HttpServletRequest request) throws Exception {
|
|
||||||
return IOUtils.toString(request.getInputStream(), UTF8);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从request中获取json。如果提交方式是application/x-www-form-urlencoded,则组装成json格式。
|
* 将get类型的参数转换成map,
|
||||||
*
|
*
|
||||||
* @param request request对象
|
* @param query charset=utf-8&biz_content=xxx
|
||||||
* @return 返回json
|
* @return
|
||||||
* @throws IOException
|
|
||||||
*/
|
*/
|
||||||
public static String getJson(HttpServletRequest request) throws Exception {
|
public static Map<String, String> parseQueryToMap(String query) {
|
||||||
String requestJson = null;
|
if (query == null) {
|
||||||
String contectType = request.getContentType();
|
|
||||||
if (StringUtils.isBlank(contectType)) {
|
|
||||||
throw ErrorEnum.ISV_INVALID_CONTENT_TYPE.getErrorMeta().getException(contectType);
|
|
||||||
}
|
|
||||||
|
|
||||||
contectType = contectType.toLowerCase();
|
|
||||||
|
|
||||||
if (contectType.contains(CONTENT_TYPE_JSON) || contectType.contains(CONTENT_TYPE_TEXT)) {
|
|
||||||
requestJson = getText(request);
|
|
||||||
} else if (contectType.contains(CONTENT_TYPE_URLENCODED)) {
|
|
||||||
Map<String, Object> params = convertRequestParamsToMap(request);
|
|
||||||
requestJson = JSON.toJSONString(params);
|
|
||||||
} else {
|
|
||||||
throw ErrorEnum.ISV_INVALID_CONTENT_TYPE.getErrorMeta().getException(contectType);
|
|
||||||
}
|
|
||||||
return requestJson;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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();
|
return Collections.emptyMap();
|
||||||
}
|
}
|
||||||
Map<String, Object> retMap = new HashMap<String, Object>(paramMap.size());
|
String[] queryList = StringUtils.split(query, '&');
|
||||||
|
Map<String, String> params = new HashMap<>(16);
|
||||||
Set<Entry<String, String[]>> entrySet = paramMap.entrySet();
|
for (String param : queryList) {
|
||||||
|
String[] paramArr = param.split("\\=");
|
||||||
for (Entry<String, String[]> entry : entrySet) {
|
if (paramArr.length == 2) {
|
||||||
String name = entry.getKey();
|
|
||||||
String[] values = entry.getValue();
|
|
||||||
if (values.length == 1) {
|
|
||||||
retMap.put(name, values[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return retMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取客户端真实IP
|
|
||||||
*
|
|
||||||
* @param request request对象
|
|
||||||
* @return 返回ip
|
|
||||||
*/
|
|
||||||
public static String getClientIP(HttpServletRequest request) {
|
|
||||||
String ipAddress = request.getHeader("x-forwarded-for");
|
|
||||||
if (ipAddress == null || ipAddress.length() == 0 || UNKOWN.equalsIgnoreCase(ipAddress)) {
|
|
||||||
ipAddress = request.getHeader("Proxy-Client-IP");
|
|
||||||
}
|
|
||||||
if (ipAddress == null || ipAddress.length() == 0 || UNKOWN.equalsIgnoreCase(ipAddress)) {
|
|
||||||
ipAddress = request.getHeader("WL-Proxy-Client-IP");
|
|
||||||
}
|
|
||||||
if (ipAddress == null || ipAddress.length() == 0 || UNKOWN.equalsIgnoreCase(ipAddress)) {
|
|
||||||
ipAddress = request.getRemoteAddr();
|
|
||||||
if (LOCAL_IP.equals(ipAddress)) {
|
|
||||||
// 根据网卡取本机配置的IP
|
|
||||||
try {
|
try {
|
||||||
InetAddress inet = InetAddress.getLocalHost();
|
params.put(paramArr[0], URLDecoder.decode(paramArr[1], UTF8));
|
||||||
ipAddress = inet.getHostAddress();
|
} catch (UnsupportedEncodingException e) {
|
||||||
} catch (UnknownHostException e) {
|
e.printStackTrace();
|
||||||
// ignore
|
|
||||||
}
|
}
|
||||||
}
|
} else if (paramArr.length == 1) {
|
||||||
|
params.put(paramArr[0], "");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
|
return params;
|
||||||
if (ipAddress != null && ipAddress.length() > IP_LEN) {
|
|
||||||
if (ipAddress.indexOf(",") > 0) {
|
|
||||||
ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ipAddress;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 是否是get请求
|
|
||||||
* @param request request对象
|
|
||||||
* @return true,是
|
|
||||||
*/
|
|
||||||
public static boolean isGetRequest(HttpServletRequest request) {
|
|
||||||
return GET.equalsIgnoreCase(request.getMethod());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,12 +1,9 @@
|
|||||||
package com.gitee.sop.gatewaycommon.validate;
|
package com.gitee.sop.gatewaycommon.validate;
|
||||||
|
|
||||||
import com.gitee.sop.gatewaycommon.bean.ApiContext;
|
|
||||||
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
|
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
|
||||||
import com.gitee.sop.gatewaycommon.param.ApiParam;
|
import com.gitee.sop.gatewaycommon.param.ApiParam;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author tanghc
|
* @author tanghc
|
||||||
@@ -24,8 +21,7 @@ public abstract class AbstractSigner implements Signer {
|
|||||||
protected abstract String buildServerSign(ApiParam params, String secret);
|
protected abstract String buildServerSign(ApiParam params, String secret);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean checkSign(HttpServletRequest request, String secret) {
|
public boolean checkSign(ApiParam apiParam, String secret) {
|
||||||
ApiParam apiParam = ApiContext.getApiParam();
|
|
||||||
String clientSign = apiParam.fetchSignAndRemove();
|
String clientSign = apiParam.fetchSignAndRemove();
|
||||||
if (StringUtils.isBlank(clientSign)) {
|
if (StringUtils.isBlank(clientSign)) {
|
||||||
throw ErrorEnum.ISV_MISSING_SIGNATURE.getErrorMeta().getException();
|
throw ErrorEnum.ISV_MISSING_SIGNATURE.getErrorMeta().getException();
|
||||||
@@ -33,4 +29,16 @@ public abstract class AbstractSigner implements Signer {
|
|||||||
String serverSign = buildServerSign(apiParam, secret);
|
String serverSign = buildServerSign(apiParam, secret);
|
||||||
return clientSign.equals(serverSign);
|
return clientSign.equals(serverSign);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String byte2hex(byte[] bytes) {
|
||||||
|
StringBuilder sign = new StringBuilder();
|
||||||
|
for (int i = 0; i < bytes.length; i++) {
|
||||||
|
String hex = Integer.toHexString(bytes[i] & 0xFF);
|
||||||
|
if (hex.length() == 1) {
|
||||||
|
sign.append("0");
|
||||||
|
}
|
||||||
|
sign.append(hex);
|
||||||
|
}
|
||||||
|
return sign.toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -3,7 +3,6 @@ package com.gitee.sop.gatewaycommon.validate;
|
|||||||
|
|
||||||
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
|
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
|
||||||
import com.gitee.sop.gatewaycommon.param.ApiParam;
|
import com.gitee.sop.gatewaycommon.param.ApiParam;
|
||||||
import org.apache.tomcat.util.buf.HexUtils;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -51,6 +50,6 @@ public class ApiSigner extends AbstractSigner {
|
|||||||
byte[] bytes = signEncipher.encrypt(source, secret);
|
byte[] bytes = signEncipher.encrypt(source, secret);
|
||||||
|
|
||||||
// 第四步:把二进制转化为大写的十六进制
|
// 第四步:把二进制转化为大写的十六进制
|
||||||
return HexUtils.toHexString(bytes).toUpperCase();
|
return byte2hex(bytes).toUpperCase();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -49,7 +49,7 @@ public class ApiValidator implements Validator {
|
|||||||
checkAppKey(param);
|
checkAppKey(param);
|
||||||
checkSign(param);
|
checkSign(param);
|
||||||
}
|
}
|
||||||
checkUploadFile(param);
|
// checkUploadFile(param);
|
||||||
checkTimeout(param);
|
checkTimeout(param);
|
||||||
checkFormat(param);
|
checkFormat(param);
|
||||||
}
|
}
|
||||||
@@ -59,28 +59,28 @@ public class ApiValidator implements Validator {
|
|||||||
*
|
*
|
||||||
* @param param
|
* @param param
|
||||||
*/
|
*/
|
||||||
protected void checkUploadFile(ApiParam param) {
|
// protected void checkUploadFile(ApiParam param) {
|
||||||
UploadContext uploadContext = ApiContext.getUploadContext();
|
// UploadContext uploadContext = ApiContext.getUploadContext();
|
||||||
if (uploadContext != null) {
|
// if (uploadContext != null) {
|
||||||
try {
|
// try {
|
||||||
List<MultipartFile> files = uploadContext.getAllFile();
|
// List<MultipartFile> files = uploadContext.getAllFile();
|
||||||
for (MultipartFile file : files) {
|
// for (MultipartFile file : files) {
|
||||||
// 客户端传来的文件md5
|
// // 客户端传来的文件md5
|
||||||
String clientMd5 = param.getString(file.getName());
|
// String clientMd5 = param.getString(file.getName());
|
||||||
if (clientMd5 != null) {
|
// if (clientMd5 != null) {
|
||||||
String fileMd5 = DigestUtils.md5Hex(file.getBytes());
|
// String fileMd5 = DigestUtils.md5Hex(file.getBytes());
|
||||||
if (!clientMd5.equals(fileMd5)) {
|
// if (!clientMd5.equals(fileMd5)) {
|
||||||
throw ErrorEnum.ISV_UPLOAD_FAIL.getErrorMeta().getException();
|
// throw ErrorEnum.ISV_UPLOAD_FAIL.getErrorMeta().getException();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
} catch (IOException e) {
|
// } catch (IOException e) {
|
||||||
logger.error("验证上传文件MD5错误", e);
|
// logger.error("验证上传文件MD5错误", e);
|
||||||
throw ErrorEnum.ISV_UPLOAD_FAIL.getErrorMeta().getException();
|
// throw ErrorEnum.ISV_UPLOAD_FAIL.getErrorMeta().getException();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
}
|
// }
|
||||||
|
|
||||||
protected void checkTimeout(ApiParam param) {
|
protected void checkTimeout(ApiParam param) {
|
||||||
int timeoutSeconds = ApiContext.getApiConfig().getTimeoutSeconds();
|
int timeoutSeconds = ApiContext.getApiConfig().getTimeoutSeconds();
|
||||||
@@ -129,7 +129,7 @@ public class ApiValidator implements Validator {
|
|||||||
throw ErrorEnum.ISV_MISSING_SIGNATURE_CONFIG.getErrorMeta().getException();
|
throw ErrorEnum.ISV_MISSING_SIGNATURE_CONFIG.getErrorMeta().getException();
|
||||||
}
|
}
|
||||||
Signer signer = apiConfig.getSigner();
|
Signer signer = apiConfig.getSigner();
|
||||||
boolean isRightSign = signer.checkSign(ApiContext.getRequest(), secret);
|
boolean isRightSign = signer.checkSign(param, secret);
|
||||||
// 错误的sign
|
// 错误的sign
|
||||||
if (!isRightSign) {
|
if (!isRightSign) {
|
||||||
throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException(param.fetchNameVersion());
|
throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException(param.fetchNameVersion());
|
||||||
|
@@ -1,5 +1,7 @@
|
|||||||
package com.gitee.sop.gatewaycommon.validate;
|
package com.gitee.sop.gatewaycommon.validate;
|
||||||
|
|
||||||
|
import com.gitee.sop.gatewaycommon.param.ApiParam;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -11,10 +13,10 @@ public interface Signer {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 签名校验
|
* 签名校验
|
||||||
* @param request
|
* @param apiParam 参数
|
||||||
* @param secret 秘钥
|
* @param secret 秘钥
|
||||||
* @return true签名正确
|
* @return true签名正确
|
||||||
*/
|
*/
|
||||||
boolean checkSign(HttpServletRequest request, String secret);
|
boolean checkSign(ApiParam apiParam, String secret);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -16,10 +16,9 @@ import javax.servlet.http.HttpServletRequest;
|
|||||||
public class AlipaySigner implements Signer {
|
public class AlipaySigner implements Signer {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean checkSign(HttpServletRequest request, String secret) {
|
public boolean checkSign(ApiParam apiParam, String secret) {
|
||||||
// 服务端存的是公钥
|
// 服务端存的是公钥
|
||||||
String publicKey = secret;
|
String publicKey = secret;
|
||||||
ApiParam apiParam = ApiContext.getApiParam();
|
|
||||||
String charset = apiParam.fetchCharset();
|
String charset = apiParam.fetchCharset();
|
||||||
String signType = apiParam.fetchSignMethod();
|
String signType = apiParam.fetchSignMethod();
|
||||||
if (signType == null) {
|
if (signType == null) {
|
||||||
|
@@ -7,8 +7,7 @@ import com.gitee.sop.gatewaycommon.validate.AbstractSigner;
|
|||||||
import com.gitee.sop.gatewaycommon.validate.SignEncipher;
|
import com.gitee.sop.gatewaycommon.validate.SignEncipher;
|
||||||
import com.gitee.sop.gatewaycommon.validate.SignEncipherHMAC_MD5;
|
import com.gitee.sop.gatewaycommon.validate.SignEncipherHMAC_MD5;
|
||||||
import com.gitee.sop.gatewaycommon.validate.SignEncipherMD5;
|
import com.gitee.sop.gatewaycommon.validate.SignEncipherMD5;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.tomcat.util.buf.HexUtils;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -59,6 +58,6 @@ public class TaobaoSigner extends AbstractSigner {
|
|||||||
byte[] bytes = signEncipher.encrypt(source, secret);
|
byte[] bytes = signEncipher.encrypt(source, secret);
|
||||||
|
|
||||||
// 第四步:把二进制转化为大写的十六进制
|
// 第四步:把二进制转化为大写的十六进制
|
||||||
return HexUtils.toHexString(bytes).toUpperCase();
|
return byte2hex(bytes).toUpperCase();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -26,10 +26,10 @@
|
|||||||
<version>1.0.0-SNAPSHOT</version>
|
<version>1.0.0-SNAPSHOT</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<!--<dependency>-->
|
||||||
<groupId>org.springframework.cloud</groupId>
|
<!--<groupId>org.springframework.cloud</groupId>-->
|
||||||
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
|
<!--<artifactId>spring-cloud-starter-netflix-zuul</artifactId>-->
|
||||||
</dependency>
|
<!--</dependency>-->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.cloud</groupId>
|
<groupId>org.springframework.cloud</groupId>
|
||||||
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
|
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
|
||||||
|
@@ -2,10 +2,10 @@ package com.gitee.sop.gateway;
|
|||||||
|
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
|
//import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
|
||||||
|
|
||||||
// 开启网关功能
|
// 开启网关功能
|
||||||
@EnableZuulProxy
|
//@EnableZuulProxy
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
public class SopGatewayApplication {
|
public class SopGatewayApplication {
|
||||||
|
|
||||||
|
@@ -0,0 +1,36 @@
|
|||||||
|
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 org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
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 {
|
||||||
|
|
||||||
|
{
|
||||||
|
Map<String, String> appSecretStore = new HashMap();
|
||||||
|
appSecretStore.put("taobao_test", "G9w0BAQEFAAOCAQ8AMIIBCgKCA");
|
||||||
|
ApiContext.getApiConfig().addAppSecret(appSecretStore);
|
||||||
|
}
|
||||||
|
}
|
@@ -1,35 +0,0 @@
|
|||||||
package com.gitee.sop.gateway.config;
|
|
||||||
|
|
||||||
import com.gitee.sop.gatewaycommon.bean.ApiContext;
|
|
||||||
import com.gitee.sop.gatewaycommon.configuration.AlipayZuulConfiguration;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 开通支付宝开放平台能力
|
|
||||||
* @author tanghc
|
|
||||||
*/
|
|
||||||
@Configuration
|
|
||||||
public class ZuulConfig extends AlipayZuulConfiguration {
|
|
||||||
|
|
||||||
{
|
|
||||||
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 ZuulConfig extends TaobaoZuulConfiguration {
|
|
||||||
//
|
|
||||||
// {
|
|
||||||
// Map<String, String> appSecretStore = new HashMap();
|
|
||||||
// appSecretStore.put("taobao_test", "G9w0BAQEFAAOCAQ8AMIIBCgKCA");
|
|
||||||
// ApiContext.getApiConfig().addAppSecret(appSecretStore);
|
|
||||||
// }
|
|
||||||
//}
|
|
@@ -1,28 +0,0 @@
|
|||||||
server.port=8081
|
|
||||||
spring.application.name=api-gateway
|
|
||||||
|
|
||||||
# 注册中心
|
|
||||||
eureka.host=localhost
|
|
||||||
eureka.port=1111
|
|
||||||
eureka.client.serviceUrl.defaultZone=http://${eureka.host}:${eureka.port}/eureka/
|
|
||||||
|
|
||||||
# 入口地址,默认是/zuul
|
|
||||||
zuul.servlet-path=/api
|
|
||||||
|
|
||||||
# 路由配置
|
|
||||||
# story服务路由配置
|
|
||||||
# 内置有个默认的:
|
|
||||||
# zuul.routes.<name>.path=/<服务名>/**
|
|
||||||
# zuul.routes.<name>.serviceId=<服务名>
|
|
||||||
# 等同于:
|
|
||||||
# zuul.routes.<服务名>=/<服务名>/**
|
|
||||||
|
|
||||||
|
|
||||||
# Redis数据库索引(默认为0)
|
|
||||||
spring.redis.database=0
|
|
||||||
# Redis服务器地址
|
|
||||||
spring.redis.host=127.0.0.1
|
|
||||||
# Redis服务器连接端口
|
|
||||||
spring.redis.port=6379
|
|
||||||
# Redis服务器连接密码(默认为空)
|
|
||||||
spring.redis.password=
|
|
16
sop-gateway/src/main/resources/application.yml
Normal file
16
sop-gateway/src/main/resources/application.yml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
server:
|
||||||
|
port: 8081
|
||||||
|
|
||||||
|
eureka:
|
||||||
|
serverAddr: localhost:1111
|
||||||
|
client:
|
||||||
|
serviceUrl:
|
||||||
|
defaultZone: http://${eureka.serverAddr}/eureka/
|
||||||
|
|
||||||
|
spring:
|
||||||
|
cloud:
|
||||||
|
gateway:
|
||||||
|
discovery:
|
||||||
|
locator:
|
||||||
|
lower-case-service-id: true
|
||||||
|
enabled: true
|
8
sop-gateway/src/main/resources/bootstrap.yml
Normal file
8
sop-gateway/src/main/resources/bootstrap.yml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
spring:
|
||||||
|
application:
|
||||||
|
name: api-gateway
|
||||||
|
|
||||||
|
cloud:
|
||||||
|
zookeeper:
|
||||||
|
connect-string: localhost:2181
|
@@ -12,7 +12,7 @@
|
|||||||
<artifactId>sop-registry</artifactId>
|
<artifactId>sop-registry</artifactId>
|
||||||
<version>1.0.0-SNAPSHOT</version>
|
<version>1.0.0-SNAPSHOT</version>
|
||||||
<name>sop-registry</name>
|
<name>sop-registry</name>
|
||||||
<description>注册中心</description>
|
<description>sop-registry</description>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<java.version>1.8</java.version>
|
<java.version>1.8</java.version>
|
||||||
|
@@ -10,8 +10,8 @@
|
|||||||
<relativePath/> <!-- lookup parent from repository -->
|
<relativePath/> <!-- lookup parent from repository -->
|
||||||
</parent>
|
</parent>
|
||||||
<groupId>com.gitee.sop</groupId>
|
<groupId>com.gitee.sop</groupId>
|
||||||
<artifactId>sop-server-common</artifactId>
|
<artifactId>sop-service-common</artifactId>
|
||||||
<name>sop-server-common</name>
|
<name>sop-service-common</name>
|
||||||
<version>1.0.0-SNAPSHOT</version>
|
<version>1.0.0-SNAPSHOT</version>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
@@ -30,6 +30,34 @@
|
|||||||
<version>${fastjson.version}</version>
|
<version>${fastjson.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- zookeeper -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-zookeeper-all</artifactId>
|
||||||
|
<exclusions>
|
||||||
|
<!-- 不使用服务发现 -->
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.apache.zookeeper</groupId>
|
||||||
|
<artifactId>zookeeper</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.zookeeper</groupId>
|
||||||
|
<artifactId>zookeeper</artifactId>
|
||||||
|
<version>3.4.12</version>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-log4j12</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-web</artifactId>
|
<artifactId>spring-boot-starter-web</artifactId>
|
@@ -1,3 +1,3 @@
|
|||||||
# sop-server-common
|
# sop-service-common
|
||||||
|
|
||||||
微服务端公共组件,每个微服务需要依赖这个jar,先把这个工程mvn deploy到maven私服。
|
微服务端公共组件,每个微服务需要依赖这个jar,先把这个工程mvn deploy到maven私服。
|
@@ -1,6 +1,7 @@
|
|||||||
package com.gitee.sop.servercommon.bean;
|
package com.gitee.sop.servercommon.bean;
|
||||||
|
|
||||||
import com.alibaba.fastjson.annotation.JSONField;
|
import com.alibaba.fastjson.annotation.JSONField;
|
||||||
|
import com.gitee.sop.servercommon.route.GatewayRouteDefinition;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -13,6 +14,7 @@ public class ServiceApiInfo {
|
|||||||
private String md5;
|
private String md5;
|
||||||
private String appName;
|
private String appName;
|
||||||
private List<ApiMeta> apis;
|
private List<ApiMeta> apis;
|
||||||
|
private List<GatewayRouteDefinition> routeDefinitionList;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public static class ApiMeta {
|
public static class ApiMeta {
|
@@ -9,9 +9,9 @@ import java.util.List;
|
|||||||
* 具备支付宝开放平台服务提供能力
|
* 具备支付宝开放平台服务提供能力
|
||||||
* @author tanghc
|
* @author tanghc
|
||||||
*/
|
*/
|
||||||
public class AlipayServerConfiguration extends BaseServerConfiguration {
|
public class AlipayServiceConfiguration extends BaseServiceConfiguration {
|
||||||
|
|
||||||
public AlipayServerConfiguration() {
|
public AlipayServiceConfiguration() {
|
||||||
// 默认版本号为1.0
|
// 默认版本号为1.0
|
||||||
ServiceConfig.getInstance().setDefaultVersion("1.0");
|
ServiceConfig.getInstance().setDefaultVersion("1.0");
|
||||||
}
|
}
|
@@ -1,44 +1,30 @@
|
|||||||
package com.gitee.sop.servercommon.configuration;
|
package com.gitee.sop.servercommon.configuration;
|
||||||
|
|
||||||
import com.gitee.sop.servercommon.bean.ServiceConfig;
|
import com.gitee.sop.servercommon.bean.ServiceConfig;
|
||||||
import com.gitee.sop.servercommon.exception.ServiceException;
|
|
||||||
import com.gitee.sop.servercommon.interceptor.ServiceContextInterceptor;
|
import com.gitee.sop.servercommon.interceptor.ServiceContextInterceptor;
|
||||||
import com.gitee.sop.servercommon.manager.ApiMetaManager;
|
import com.gitee.sop.servercommon.manager.ApiMetaManager;
|
||||||
import com.gitee.sop.servercommon.manager.DefaultRequestMappingEvent;
|
import com.gitee.sop.servercommon.manager.DefaultRequestMappingEvent;
|
||||||
import com.gitee.sop.servercommon.manager.RedisApiMetaManager;
|
import com.gitee.sop.servercommon.manager.ServiceZookeeperApiMetaManager;
|
||||||
import com.gitee.sop.servercommon.mapping.ApiMappingHandlerMapping;
|
import com.gitee.sop.servercommon.mapping.ApiMappingHandlerMapping;
|
||||||
import com.gitee.sop.servercommon.message.ServiceErrorFactory;
|
import com.gitee.sop.servercommon.message.ServiceErrorFactory;
|
||||||
import com.gitee.sop.servercommon.result.ServiceResultBuilder;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.web.servlet.error.ErrorController;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.core.env.Environment;
|
import org.springframework.core.env.Environment;
|
||||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
|
||||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
|
||||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.ResponseBody;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
|
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author tanghc
|
* @author tanghc
|
||||||
*/
|
*/
|
||||||
public class BaseServerConfiguration extends WebMvcConfigurationSupport {
|
public class BaseServiceConfiguration extends WebMvcConfigurationSupport {
|
||||||
|
|
||||||
public BaseServerConfiguration() {
|
public BaseServiceConfiguration() {
|
||||||
ServiceConfig.getInstance().getI18nModules().add("i18n/isp/bizerror");
|
ServiceConfig.getInstance().getI18nModules().add("i18n/isp/bizerror");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private StringRedisTemplate redisTemplate;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private Environment environment;
|
private Environment environment;
|
||||||
|
|
||||||
@@ -55,7 +41,7 @@ public class BaseServerConfiguration extends WebMvcConfigurationSupport {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected RequestMappingHandlerMapping createRequestMappingHandlerMapping() {
|
protected RequestMappingHandlerMapping createRequestMappingHandlerMapping() {
|
||||||
ApiMetaManager apiMetaManager = new RedisApiMetaManager(redisTemplate);
|
ApiMetaManager apiMetaManager = new ServiceZookeeperApiMetaManager(environment);
|
||||||
DefaultRequestMappingEvent requestMappingEvent = new DefaultRequestMappingEvent(apiMetaManager, environment);
|
DefaultRequestMappingEvent requestMappingEvent = new DefaultRequestMappingEvent(apiMetaManager, environment);
|
||||||
return new ApiMappingHandlerMapping(requestMappingEvent);
|
return new ApiMappingHandlerMapping(requestMappingEvent);
|
||||||
}
|
}
|
@@ -3,6 +3,7 @@ package com.gitee.sop.servercommon.configuration;
|
|||||||
import com.gitee.sop.servercommon.bean.ServiceConfig;
|
import com.gitee.sop.servercommon.bean.ServiceConfig;
|
||||||
import com.gitee.sop.servercommon.exception.ServiceException;
|
import com.gitee.sop.servercommon.exception.ServiceException;
|
||||||
import com.gitee.sop.servercommon.result.ServiceResultBuilder;
|
import com.gitee.sop.servercommon.result.ServiceResultBuilder;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
@@ -16,6 +17,7 @@ import javax.servlet.http.HttpServletResponse;
|
|||||||
* @author tanghc
|
* @author tanghc
|
||||||
*/
|
*/
|
||||||
@ControllerAdvice
|
@ControllerAdvice
|
||||||
|
@Slf4j
|
||||||
public class DefaultGlobalExceptionHandler implements GlobalExceptionHandler {
|
public class DefaultGlobalExceptionHandler implements GlobalExceptionHandler {
|
||||||
|
|
||||||
@RequestMapping("/error")
|
@RequestMapping("/error")
|
||||||
@@ -27,10 +29,17 @@ public class DefaultGlobalExceptionHandler implements GlobalExceptionHandler {
|
|||||||
|
|
||||||
@ExceptionHandler(ServiceException.class)
|
@ExceptionHandler(ServiceException.class)
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public Object exceptionHandler(HttpServletRequest request, HttpServletResponse response, Exception exception) throws Exception {
|
public Object serviceExceptionHandler(HttpServletRequest request, HttpServletResponse response, Exception exception) throws Exception {
|
||||||
return this.processError(request, response, exception);
|
return this.processError(request, response, exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(Exception.class)
|
||||||
|
@ResponseBody
|
||||||
|
public Object exceptionHandler(HttpServletRequest request, HttpServletResponse response, Exception exception) throws Exception {
|
||||||
|
log.error("系统错误", exception);
|
||||||
|
return this.processError(request, response, new RuntimeException("系统错误"));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理异常
|
* 处理异常
|
||||||
*
|
*
|
@@ -4,6 +4,6 @@ package com.gitee.sop.servercommon.configuration;
|
|||||||
* 具备淘宝开放平台服务提供能力
|
* 具备淘宝开放平台服务提供能力
|
||||||
* @author tanghc
|
* @author tanghc
|
||||||
*/
|
*/
|
||||||
public class TaobaoServerConfiguration extends BaseServerConfiguration {
|
public class TaobaoServiceConfiguration extends BaseServiceConfiguration {
|
||||||
|
|
||||||
}
|
}
|
@@ -0,0 +1,153 @@
|
|||||||
|
package com.gitee.sop.servercommon.manager;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import com.gitee.sop.servercommon.bean.ServiceApiInfo;
|
||||||
|
import com.gitee.sop.servercommon.route.GatewayPredicateDefinition;
|
||||||
|
import com.gitee.sop.servercommon.route.GatewayRouteDefinition;
|
||||||
|
import com.gitee.sop.servercommon.route.ServiceRouteInfo;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.curator.framework.CuratorFramework;
|
||||||
|
import org.apache.curator.framework.CuratorFrameworkFactory;
|
||||||
|
import org.apache.curator.retry.ExponentialBackoffRetry;
|
||||||
|
import org.springframework.core.env.Environment;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author tanghc
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class ServiceZookeeperApiMetaManager implements ApiMetaManager {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NameVersion=alipay.story.get1.0
|
||||||
|
* see com.gitee.sop.gatewaycommon.route.NameVersionRoutePredicateFactory
|
||||||
|
*/
|
||||||
|
private static String QUERY_PREDICATE_DEFINITION_TPL = "NameVersion=%s";
|
||||||
|
|
||||||
|
private static ServiceApiInfo.ApiMeta FIRST_API_META = new ServiceApiInfo.ApiMeta("_" + System.currentTimeMillis() + "_", "/", "0.0");
|
||||||
|
|
||||||
|
private String sopServiceApiPath = "/sop-service-api";
|
||||||
|
|
||||||
|
private Environment environment;
|
||||||
|
|
||||||
|
public ServiceZookeeperApiMetaManager(Environment environment) {
|
||||||
|
this.environment = environment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void uploadApi(ServiceApiInfo serviceApiInfo) {
|
||||||
|
ServiceRouteInfo serviceRouteInfo = this.buildServiceGatewayInfo(serviceApiInfo);
|
||||||
|
this.uploadServiceRouteInfoToZookeeper(serviceRouteInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建接口信息,符合spring cloud gateway的格式
|
||||||
|
*
|
||||||
|
* @param serviceApiInfo
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
protected ServiceRouteInfo buildServiceGatewayInfo(ServiceApiInfo serviceApiInfo) {
|
||||||
|
List<ServiceApiInfo.ApiMeta> apis = serviceApiInfo.getApis();
|
||||||
|
List<GatewayRouteDefinition> routeDefinitionList = new ArrayList<>(apis.size() + 1);
|
||||||
|
routeDefinitionList.add(this.buildReadBodyRouteDefinition(serviceApiInfo));
|
||||||
|
for (ServiceApiInfo.ApiMeta apiMeta : apis) {
|
||||||
|
GatewayRouteDefinition gatewayRouteDefinition = this.buildGatewayRouteDefinition(serviceApiInfo, apiMeta);
|
||||||
|
routeDefinitionList.add(gatewayRouteDefinition);
|
||||||
|
}
|
||||||
|
ServiceRouteInfo serviceRouteInfo = new ServiceRouteInfo();
|
||||||
|
serviceRouteInfo.setAppName(serviceApiInfo.getAppName());
|
||||||
|
serviceRouteInfo.setRouteDefinitionList(routeDefinitionList);
|
||||||
|
serviceRouteInfo.setMd5(serviceApiInfo.getMd5());
|
||||||
|
return serviceRouteInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加com.gitee.sop.gatewaycommon.route.ReadBodyRoutePredicateFactory,解决form表单获取不到问题
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
protected GatewayRouteDefinition buildReadBodyRouteDefinition(ServiceApiInfo serviceApiInfo) {
|
||||||
|
GatewayRouteDefinition gatewayRouteDefinition = this.buildGatewayRouteDefinition(serviceApiInfo, FIRST_API_META);
|
||||||
|
|
||||||
|
GatewayPredicateDefinition gatewayPredicateDefinition = new GatewayPredicateDefinition();
|
||||||
|
gatewayPredicateDefinition.setName("ReadBody");
|
||||||
|
GatewayPredicateDefinition readerBodyPredicateDefinition = this.buildNameVersionPredicateDefinition(FIRST_API_META);
|
||||||
|
List<GatewayPredicateDefinition> predicates = Arrays.asList(gatewayPredicateDefinition, readerBodyPredicateDefinition);
|
||||||
|
gatewayRouteDefinition.setPredicates(predicates);
|
||||||
|
|
||||||
|
return gatewayRouteDefinition;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected GatewayRouteDefinition buildGatewayRouteDefinition(ServiceApiInfo serviceApiInfo, ServiceApiInfo.ApiMeta apiMeta) {
|
||||||
|
GatewayRouteDefinition gatewayRouteDefinition = new GatewayRouteDefinition();
|
||||||
|
// 唯一id规则:接口名 + 版本号
|
||||||
|
gatewayRouteDefinition.setId(apiMeta.fetchNameVersion());
|
||||||
|
gatewayRouteDefinition.setFilters(Collections.emptyList());
|
||||||
|
gatewayRouteDefinition.setOrder(0);
|
||||||
|
List<GatewayPredicateDefinition> predicates = Arrays.asList(this.buildNameVersionPredicateDefinition(apiMeta));
|
||||||
|
gatewayRouteDefinition.setPredicates(predicates);
|
||||||
|
// lb://story-service#/alipay.story.get/
|
||||||
|
String servletPath = "#" + apiMeta.getPath();
|
||||||
|
gatewayRouteDefinition.setUri("lb://" + serviceApiInfo.getAppName() + servletPath);
|
||||||
|
return gatewayRouteDefinition;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected GatewayPredicateDefinition buildNameVersionPredicateDefinition(ServiceApiInfo.ApiMeta apiMeta) {
|
||||||
|
return new GatewayPredicateDefinition(String.format(QUERY_PREDICATE_DEFINITION_TPL, apiMeta.fetchNameVersion()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传接口信息到zookeeper
|
||||||
|
*
|
||||||
|
* @param serviceRouteInfo
|
||||||
|
*/
|
||||||
|
protected void uploadServiceRouteInfoToZookeeper(ServiceRouteInfo serviceRouteInfo) {
|
||||||
|
String zookeeperServerAddr = environment.getProperty("spring.cloud.zookeeper.connect-string");
|
||||||
|
if (StringUtils.isEmpty(zookeeperServerAddr)) {
|
||||||
|
throw new RuntimeException("未指定spring.cloud.zookeeper.connect-string参数");
|
||||||
|
}
|
||||||
|
String serviceRouteInfoJson = JSON.toJSONString(serviceRouteInfo);
|
||||||
|
CuratorFramework client = null;
|
||||||
|
try {
|
||||||
|
// 保存路径
|
||||||
|
String savePath = sopServiceApiPath + "/" + serviceRouteInfo.getAppName();
|
||||||
|
|
||||||
|
client = CuratorFrameworkFactory.builder()
|
||||||
|
.connectString(zookeeperServerAddr)
|
||||||
|
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
client.start();
|
||||||
|
|
||||||
|
log.info("上传接口信息到zookeeper,path:{}, appName:{}, md5:{}, 接口数量:{}",
|
||||||
|
savePath,
|
||||||
|
serviceRouteInfo.getAppName(),
|
||||||
|
serviceRouteInfo.getMd5(),
|
||||||
|
serviceRouteInfo.getRouteDefinitionList().size());
|
||||||
|
|
||||||
|
client.create()
|
||||||
|
// 如果节点存在则Curator将会使用给出的数据设置这个节点的值
|
||||||
|
.orSetData()
|
||||||
|
// 如果指定节点的父节点不存在,则Curator将会自动级联创建父节点
|
||||||
|
.creatingParentContainersIfNeeded()
|
||||||
|
.forPath(savePath, serviceRouteInfoJson.getBytes());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("更新接口信息到zookeeper失败, appName:{}", serviceRouteInfo.getAppName(), e);
|
||||||
|
} finally {
|
||||||
|
if (client != null) {
|
||||||
|
client.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user