diff --git a/doc/docs/files/10096_使用SpringCloudGateway.md b/doc/docs/files/10096_使用SpringCloudGateway.md index dc74bbc6..cdad27e0 100644 --- a/doc/docs/files/10096_使用SpringCloudGateway.md +++ b/doc/docs/files/10096_使用SpringCloudGateway.md @@ -1,92 +1,16 @@ # 使用SpringCloudGateway -SOP默认网关是使用Spring Cloud Zuul,您也可以切换成Spring Cloud Gateway,完整代码见`spring-cloud-gateway`分支。 - -如果您的系统并发量不大,建议使用zuul,因为zuul的功能更全面,有新功能会优先实现在zuul上。 - -- SOP中 Spring Cloud Zuul 和 Spring Cloud Gateway功能对比 - -| 功能 | Spring Cloud Zuul | Spring Cloud Gateway | -| ----- | ---- | ----------------------- | -| 签名验证|√ | √ | -| 统一异常处理|√ | √ | -| 统一返回内容|√ | √ | -| session管理|√ | √ | -| 秘钥管理|√ | √ | -| 微服务端自动验证(JSR-303)|√ | √ | -| 文件上传|√ | √ | -| 文件下载|√ | √ | -| 接口限流|√ | √ | -| 文档整合|√ | √ | -| 应用授权|√ | √ | -| 监控日志|√ | √ | -| 支持nacos|√ | √ | -| 网关动态修改参数|√ | √ | - -使用Spring Cloud Gateway步骤如下: - -- 打开sop-gateway/pom.xml,注释spring cloud zuul依赖,打开Spring Cloud Gateway依赖 +修改`sop-gateway/pom.xml`配置,artifactId部分改成`sop-bridge-gateway`即可 ```xml - - - - - - - org.springframework.cloud - spring-cloud-starter-gateway - - org.springframework.boot - spring-boot-starter-webflux + com.gitee.sop + + + + sop-bridge-gateway + 2.5.10-SNAPSHOT - - org.springframework.boot - spring-boot-starter-actuator - - - -``` - -- 打开启动类`SopGatewayApplication.java`, 注释zuul相关注解 - -```java -package com.gitee.sop.gateway; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -//import org.springframework.cloud.netflix.zuul.EnableZuulProxy; - -// 开启网关功能 -//@EnableZuulProxy -@SpringBootApplication -public class SopGatewayApplication { - - public static void main(String[] args) { - SpringApplication.run(SopGatewayApplication.class, args); - } - -} -``` - -- 禁用ZuulConfig类,注释掉@Configuration注解即可 - -```java -//@Configuration -public class ZuulConfig extends AlipayZuulConfiguration {...} -``` - -- 启用GatewayConfig类,打开@Configuration注释 - -```java -@Configuration -public class GatewayConfig extends AlipayGatewayConfiguration {...} ``` 修改完毕,重启sop-gateway diff --git a/doc/docs/files/10110_预发布灰度发布.md b/doc/docs/files/10110_预发布灰度发布.md index 74370b5c..f850baee 100644 --- a/doc/docs/files/10110_预发布灰度发布.md +++ b/doc/docs/files/10110_预发布灰度发布.md @@ -11,7 +11,7 @@ |open1.domain.com |网关服务器1 | |openpre.domain.com | 网关服务器2,作为预发布请求入口| -线上网关入口为`http://open.domain.com/api`,请求网关`http://open.domain.com/api`会负载均衡到这两台服务器 +线上网关入口为`http://open.domain.com/`,请求网关`http://open.domain.com/`会负载均衡到这两台服务器 SOP开启预发布步骤如下: @@ -26,18 +26,16 @@ pre.domain=openpre.domain.com 微服务启动参数添加:`--spring.cloud.nacos.discovery.metadata.env=pre`(eureka下是:`--eureka.instance.metadata-map.env=pre`)。 建议线上配两套启动脚本,其中预发布启动脚本添加启动参数`--eureka.instance.metadata-map.env=pre` -登录SOP-Admin,在服务列表中点击预发布,然后预发布请求地址变成:`http://openpre.domain.com/api`。 +登录SOP-Admin,在服务列表中点击预发布,然后预发布请求地址变成:`http://openpre.domain.com/`。 从`openpre.domain.com`请求进来的用户都会进预发布服务器,其它情况都走非预发布服务器。 ## 使用灰度发布 灰度发布可允许指定的用户访问灰度服务器,其它用户还是走正常流程。 -微服务启动参数添加:`--spring.cloud.nacos.discovery.metadata.env=gray`(eureka下是:`--eureka.instance.metadata-map.env=gray`)。 +登录SOP-Admin,前往`服务列表`。 -登录SOP-Admin,前往服务列表。 - -- 先设置灰度参数,指定灰度用户和灰度接口 +- 先设置灰度参数,指定灰度appId和灰度接口 - 服务器实例开启灰度 参考类: diff --git a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/LogApi.java b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/LogApi.java index 9b16cf4e..e59d49c7 100644 --- a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/LogApi.java +++ b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/LogApi.java @@ -41,13 +41,15 @@ public class LogApi { public static final String LOG_MONITOR_INSTANCE = "log.monitor.instance"; public static final String CODE_SUCCESS = "10000"; private static final String CODE_KEY = "code"; + public static final String SOP_LIST_ERRORS_PATH = "/sop/listErrors"; + public static final String SOP_CLEAR_ERRORS_PATH = "/sop/clearErrors"; @Autowired ConfigCommonMapper configCommonMapper; RestTemplate restTemplate = new RestTemplate(); - @Value("${zuul.secret}") + @Value("${sop.secret}") private String secret; @Api(name = "monitor.log.list") @@ -66,7 +68,7 @@ public class LogApi { logMonitorInstanceVOParent.setMonitorName(configCommon.getContent()); ret.add(logMonitorInstanceVOParent); try { - String logData = this.requestLogServer(ipPort, "listErrors"); + String logData = this.requestLogServer(ipPort, SOP_LIST_ERRORS_PATH); JSONObject jsonObject = JSON.parseObject(logData); if (CODE_SUCCESS.equals(jsonObject.getString("code"))) { int errorTotal = 0; @@ -97,7 +99,7 @@ public class LogApi { } try { String ipPort = configCommon.getConfigKey(); - this.requestLogServer(ipPort, "clearErrors"); + this.requestLogServer(ipPort, SOP_CLEAR_ERRORS_PATH); } catch (Exception e) { throw new BizException("清除失败"); } diff --git a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/service/ConfigPushService.java b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/service/ConfigPushService.java index d5f2d023..c2b0e943 100644 --- a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/service/ConfigPushService.java +++ b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/service/ConfigPushService.java @@ -29,7 +29,7 @@ import java.util.stream.Stream; @Service public class ConfigPushService { - private static final String GATEWAY_PUSH_URL = "http://%s/configChannelMsg"; + private static final String GATEWAY_PUSH_URL = "http://%s/sop/configChannelMsg"; private static final String API_GATEWAY_SERVICE_ID = "sop-gateway"; private static HttpTool httpTool = new HttpTool(); @@ -40,7 +40,7 @@ public class ConfigPushService { @Value("${gateway.host:}") private String gatewayHost; - @Value("${zuul.secret}") + @Value("${sop.secret}") private String secret; public void publishConfig(String dataId, String groupId, ChannelMsg channelMsg) { @@ -59,6 +59,11 @@ public class ConfigPushService { private void pushByHost(Collection hosts, GatewayPushDTO gatewayPushDTO) { for (String host : hosts) { String url = String.format(GATEWAY_PUSH_URL, host); + log.info("推送配置, dataId={}, groupId={}, operation={}, url={}", + gatewayPushDTO.getDataId() + , gatewayPushDTO.getGroupId() + , gatewayPushDTO.getChannelMsg().getOperation() + , url); try { String requestBody = JSON.toJSONString(gatewayPushDTO); Map header = new HashMap<>(8); diff --git a/sop-admin/sop-admin-server/src/main/resources/application-dev.properties b/sop-admin/sop-admin-server/src/main/resources/application-dev.properties index d3995efc..2588e1a7 100644 --- a/sop-admin/sop-admin-server/src/main/resources/application-dev.properties +++ b/sop-admin/sop-admin-server/src/main/resources/application-dev.properties @@ -38,4 +38,4 @@ mybatis.fill.com.gitee.fastmybatis.core.support.DateFillInsert=gmt_create mybatis.fill.com.gitee.fastmybatis.core.support.DateFillUpdate=gmt_modified # 不用改,如果要改,请全局替换修改 -zuul.secret=MZZOUSTua6LzApIWXCwEgbBmxSzpzC +sop.secret=MZZOUSTua6LzApIWXCwEgbBmxSzpzC diff --git a/sop-common/pom.xml b/sop-common/pom.xml index 98e0cf7c..09743364 100644 --- a/sop-common/pom.xml +++ b/sop-common/pom.xml @@ -36,7 +36,9 @@ sop-gateway-common sop-service-common - + sop-bridge-zuul + sop-bridge-gateway + diff --git a/sop-common/sop-bridge-gateway/pom.xml b/sop-common/sop-bridge-gateway/pom.xml new file mode 100644 index 00000000..328eaf22 --- /dev/null +++ b/sop-common/sop-bridge-gateway/pom.xml @@ -0,0 +1,35 @@ + + + + sop-common + com.gitee.sop + 2.5.10-SNAPSHOT + + 4.0.0 + 2.5.10-SNAPSHOT + + sop-bridge-gateway + + + + com.gitee.sop + sop-gateway-common + 2.5.10-SNAPSHOT + + + + org.springframework.cloud + spring-cloud-starter-gateway + + + org.springframework.boot + spring-boot-starter-webflux + + + org.springframework.boot + spring-boot-starter-actuator + + + \ No newline at end of file diff --git a/sop-common/sop-bridge-gateway/src/main/java/com/gitee/sop/bridge/SopGatewayAutoConfiguration.java b/sop-common/sop-bridge-gateway/src/main/java/com/gitee/sop/bridge/SopGatewayAutoConfiguration.java new file mode 100644 index 00000000..d38488fd --- /dev/null +++ b/sop-common/sop-bridge-gateway/src/main/java/com/gitee/sop/bridge/SopGatewayAutoConfiguration.java @@ -0,0 +1,12 @@ +package com.gitee.sop.bridge; + +import com.gitee.sop.gatewaycommon.gateway.configuration.AlipayGatewayConfiguration; +import org.springframework.context.annotation.Import; + +/** + * https://blog.csdn.net/seashouwang/article/details/80299571 + * @author tanghc + */ +@Import(AlipayGatewayConfiguration.class) +public class SopGatewayAutoConfiguration { +} diff --git a/sop-common/sop-bridge-gateway/src/main/resources/sop-bridge.properties b/sop-common/sop-bridge-gateway/src/main/resources/sop-bridge.properties new file mode 100644 index 00000000..c3106eb8 --- /dev/null +++ b/sop-common/sop-bridge-gateway/src/main/resources/sop-bridge.properties @@ -0,0 +1,25 @@ +# 不用改,如果要改,请全局替换修改 +sop.secret=MZZOUSTua6LzApIWXCwEgbBmxSzpzC + +# https://blog.csdn.net/qq_36872046/article/details/81058045 +# 路由转发超时时间,毫秒,默认值1000,详见:RibbonClientConfiguration.DEFAULT_READ_TIMEOUT。 +# 如果微服务端 处理时间过长,会导致ribbon read超时,解决办法将这个值调大一点 +ribbon.ReadTimeout=2000 +# 设置为true(默认false),则所有请求都重试,默认只支持get请求重试 +# 请谨慎设置,因为post请求大多都是写入请求,如果要支持重试,确保服务的幂等性 +ribbon.OkToRetryOnAllOperations=false + +spring.cloud.gateway.discovery.locator.lower-case-service-id=true +spring.cloud.gateway.discovery.locator.enabled=true + +# 不用改 +mybatis.fill.com.gitee.fastmybatis.core.support.DateFillInsert=gmt_create +mybatis.fill.com.gitee.fastmybatis.core.support.DateFillUpdate=gmt_modified + +# 文件上传配置 +spring.servlet.multipart.enabled=true +# 这里设置大一点没关系,真实大小由upload.max-file-size控制 +spring.servlet.multipart.max-file-size=50MB + +# 允许上传文件大小,不能超过这个值,单位:B,KB,MB +upload.max-file-size=2MB \ No newline at end of file diff --git a/sop-common/sop-bridge-zuul/pom.xml b/sop-common/sop-bridge-zuul/pom.xml new file mode 100644 index 00000000..1fa813b4 --- /dev/null +++ b/sop-common/sop-bridge-zuul/pom.xml @@ -0,0 +1,31 @@ + + + + sop-common + com.gitee.sop + 2.5.10-SNAPSHOT + + 4.0.0 + 2.5.10-SNAPSHOT + + sop-bridge-zuul + + + + com.gitee.sop + sop-gateway-common + 2.5.10-SNAPSHOT + + + + org.springframework.cloud + spring-cloud-starter-netflix-zuul + + + org.springframework.retry + spring-retry + + + \ No newline at end of file diff --git a/sop-common/sop-bridge-zuul/src/main/java/com/gitee/sop/bridge/SopGatewayAutoConfiguration.java b/sop-common/sop-bridge-zuul/src/main/java/com/gitee/sop/bridge/SopGatewayAutoConfiguration.java new file mode 100644 index 00000000..05762ea2 --- /dev/null +++ b/sop-common/sop-bridge-zuul/src/main/java/com/gitee/sop/bridge/SopGatewayAutoConfiguration.java @@ -0,0 +1,14 @@ +package com.gitee.sop.bridge; + +import com.gitee.sop.gatewaycommon.zuul.configuration.AlipayZuulConfiguration; +import org.springframework.cloud.netflix.zuul.EnableZuulProxy; +import org.springframework.context.annotation.Import; + +/** + * https://blog.csdn.net/seashouwang/article/details/80299571 + * @author tanghc + */ +@EnableZuulProxy +@Import(AlipayZuulConfiguration.class) +public class SopGatewayAutoConfiguration { +} diff --git a/sop-common/sop-bridge-zuul/src/main/resources/sop-bridge.properties b/sop-common/sop-bridge-zuul/src/main/resources/sop-bridge.properties new file mode 100644 index 00000000..3a83a34f --- /dev/null +++ b/sop-common/sop-bridge-zuul/src/main/resources/sop-bridge.properties @@ -0,0 +1,27 @@ +# 入口地址,不用改,默认是/zuul +zuul.servlet-path=/api +# 禁用默认的过滤器,不能删,不用改 +zuul.FormBodyWrapperFilter.pre.disable=true +zuul.Servlet30WrapperFilter.pre.disable=true +# 不用改,如果要改,请全局替换修改 +sop.secret=MZZOUSTua6LzApIWXCwEgbBmxSzpzC + +# https://blog.csdn.net/qq_36872046/article/details/81058045 +# 路由转发超时时间,毫秒,默认值1000,详见:RibbonClientConfiguration.DEFAULT_READ_TIMEOUT。 +# 如果微服务端 处理时间过长,会导致ribbon read超时,解决办法将这个值调大一点 +ribbon.ReadTimeout=2000 +# 设置为true(默认false),则所有请求都重试,默认只支持get请求重试 +# 请谨慎设置,因为post请求大多都是写入请求,如果要支持重试,确保服务的幂等性 +ribbon.OkToRetryOnAllOperations=false + +# 不用改 +mybatis.fill.com.gitee.fastmybatis.core.support.DateFillInsert=gmt_create +mybatis.fill.com.gitee.fastmybatis.core.support.DateFillUpdate=gmt_modified + +# 文件上传配置 +spring.servlet.multipart.enabled=true +# 这里设置大一点没关系,真实大小由upload.max-file-size控制 +spring.servlet.multipart.max-file-size=50MB + +# 允许上传文件大小,不能超过这个值,单位:B,KB,MB +upload.max-file-size=2MB diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ApiConfig.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ApiConfig.java index 34ee0bb1..d348be42 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ApiConfig.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ApiConfig.java @@ -1,7 +1,5 @@ package com.gitee.sop.gatewaycommon.bean; -import com.gitee.sop.gatewaycommon.gateway.param.GatewayParamBuilder; -import com.gitee.sop.gatewaycommon.gateway.result.GatewayResult; import com.gitee.sop.gatewaycommon.gateway.result.GatewayResultExecutor; import com.gitee.sop.gatewaycommon.limit.DefaultLimitManager; import com.gitee.sop.gatewaycommon.limit.LimitManager; @@ -60,7 +58,7 @@ public class ApiConfig { /** * gateway合并结果处理 */ - private ResultExecutor gatewayResultExecutor = new GatewayResultExecutor(); + private ResultExecutor gatewayResultExecutor = new GatewayResultExecutor(); /** * zuul合并结果处理 @@ -82,11 +80,6 @@ public class ApiConfig { */ private Signer signer = new ApiSigner(); - /** - * 参数解析,gateway - */ - private ParamBuilder gatewayParamBuilder = new GatewayParamBuilder(); - /** * 参数解析,zuul */ diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/BaseErrorLogController.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/BaseErrorLogController.java new file mode 100644 index 00000000..3505efc0 --- /dev/null +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/BaseErrorLogController.java @@ -0,0 +1,64 @@ +package com.gitee.sop.gatewaycommon.bean; + +import com.gitee.sop.gatewaycommon.manager.ServiceErrorManager; +import com.gitee.sop.gatewaycommon.param.ApiParam; +import com.gitee.sop.gatewaycommon.result.ApiResult; +import com.gitee.sop.gatewaycommon.result.JsonResult; +import com.gitee.sop.gatewaycommon.validate.taobao.TaobaoSigner; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.GetMapping; + +import java.util.Collection; + +/** + * @author tanghc + */ +public abstract class BaseErrorLogController { + + TaobaoSigner signer = new TaobaoSigner(); + + @Value("${sop.secret}") + private String secret; + + protected abstract ApiParam getApiParam(T t); + + @GetMapping("/sop/listErrors") + public ApiResult listErrors(T request) { + try { + this.check(request); + ServiceErrorManager serviceErrorManager = ApiConfig.getInstance().getServiceErrorManager(); + Collection allErrors = serviceErrorManager.listAllErrors(); + JsonResult apiResult = new JsonResult(); + apiResult.setData(allErrors); + return apiResult; + } catch (Exception e) { + ApiResult apiResult = new ApiResult(); + apiResult.setCode("505050"); + apiResult.setMsg(e.getMessage()); + return apiResult; + } + } + + @GetMapping("/sop/clearErrors") + public ApiResult clearErrors(T request) { + try { + this.check(request); + ServiceErrorManager serviceErrorManager = ApiConfig.getInstance().getServiceErrorManager(); + serviceErrorManager.clear(); + return new ApiResult(); + } catch (Exception e) { + ApiResult apiResult = new ApiResult(); + apiResult.setCode("505050"); + apiResult.setMsg(e.getMessage()); + return apiResult; + } + } + + protected void check(T request) { + ApiParam apiParam = getApiParam(request); + boolean right = signer.checkSign(apiParam, secret); + if (!right) { + throw new RuntimeException("签名校验失败"); + } + } +} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/env/SopEnvironmentPostProcessor.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/env/SopEnvironmentPostProcessor.java new file mode 100644 index 00000000..a4122b3a --- /dev/null +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/env/SopEnvironmentPostProcessor.java @@ -0,0 +1,39 @@ +package com.gitee.sop.gatewaycommon.env; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.env.EnvironmentPostProcessor; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.PropertiesPropertySource; +import org.springframework.core.env.PropertySource; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; + +import java.io.IOException; +import java.util.Properties; + +/** + * 自定义环境处理,在运行SpringApplication之前加载任意配置文件到Environment环境中 + */ +public class SopEnvironmentPostProcessor implements EnvironmentPostProcessor { + + private final Properties properties = new Properties(); + + @Override + public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { + Resource resource = new ClassPathResource("sop-bridge.properties"); + // 加载成PropertySource对象,并添加到Environment环境中 + environment.getPropertySources().addLast(loadProfiles(resource)); + } + + private PropertySource loadProfiles(Resource resource) { + if (resource == null || !resource.exists()) { + throw new IllegalArgumentException("资源" + resource + "不存在"); + } + try { + properties.load(resource.getInputStream()); + return new PropertiesPropertySource(resource.getFilename(), properties); + } catch (IOException ex) { + throw new IllegalStateException("加载配置文件失败" + resource, ex); + } + } +} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/ServerWebExchangeUtil.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/ServerWebExchangeUtil.java index 688db17e..631fe2b4 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/ServerWebExchangeUtil.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/ServerWebExchangeUtil.java @@ -1,6 +1,7 @@ package com.gitee.sop.gatewaycommon.gateway; import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; import com.gitee.sop.gatewaycommon.bean.SopConstants; import com.gitee.sop.gatewaycommon.gateway.common.FileUploadHttpServletRequest; import com.gitee.sop.gatewaycommon.gateway.common.RequestContentDataExtractor; @@ -13,107 +14,177 @@ import org.apache.commons.lang3.StringUtils; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; +import org.springframework.http.codec.HttpMessageReader; import org.springframework.http.converter.FormHttpMessageConverter; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.util.MultiValueMap; import org.springframework.web.multipart.MultipartHttpServletRequest; import org.springframework.web.multipart.commons.CommonsMultipartResolver; +import org.springframework.web.reactive.function.server.HandlerStrategies; +import org.springframework.web.reactive.function.server.ServerRequest; import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.WebFilterChain; +import reactor.core.publisher.Mono; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.function.Consumer; -import static com.gitee.sop.gatewaycommon.bean.SopConstants.CACHE_REQUEST_BODY_FOR_MAP; -import static com.gitee.sop.gatewaycommon.bean.SopConstants.CACHE_REQUEST_BODY_OBJECT_KEY; - /** * @author tanghc */ @Slf4j public class ServerWebExchangeUtil { + private static final String UNKNOWN_PATH = "/sop/unknown"; + private static final String VALIDATE_ERROR_PATH = "/sop/validateError"; + private static final String REST_PATH = "/rest"; + private static FormHttpMessageConverter formHttpMessageConverter = new FormHttpMessageConverter(); + private static final List> messageReaders; + + static { + messageReaders = HandlerStrategies.withDefaults().messageReaders(); + } + /** - * 获取请求参数 + * 重定向 * - * @param exchange ServerWebExchange - * @return 返回请求参数 + * @param exchange exchange + * @param apiParam apiParam + * @return 返回新的ServerWebExchange,配合chain.filter(newExchange);使用 */ - public static ApiParam getApiParam(ServerWebExchange exchange) { - return exchange.getAttribute(SopConstants.CACHE_API_PARAM); + public static ServerWebExchange getForwardExchange(ServerWebExchange exchange, ApiParam apiParam) { + ServerHttpRequest newRequest = getForwardRequest(exchange.getRequest(), apiParam); + return exchange.mutate().request(newRequest).build(); + } + + public static ServerHttpRequest getForwardRequest(ServerHttpRequest request, ApiParam apiParam) { + return request + .mutate() + .header(SopConstants.REDIRECT_VERSION_KEY, apiParam.fetchVersion()) + .path(getForwardPath(apiParam)).build(); } /** - * 设置请求参数 + * 构建一个接受请求体的request * - * @param exchange ServerWebExchange - * @param apiParam 请求参数 + * @param exchange exchange + * @return 返回ServerRequest */ - public static void setApiParam(ServerWebExchange exchange, ApiParam apiParam) { - exchange.getAttributes().put(SopConstants.CACHE_API_PARAM, apiParam); + public static ServerRequest createReadBodyRequest(ServerWebExchange exchange) { + return ServerRequest.create(exchange, messageReaders); + } + + public static ServerWebExchange getRestfulExchange(ServerWebExchange exchange, String path) { + int index = path.indexOf(REST_PATH); + // 取"/rest"的后面部分 + String newPath = path.substring(index + REST_PATH.length()); + ApiParam apiParam = new ApiParam(); + apiParam.setName(newPath); + apiParam.setVersion(""); + setApiParam(exchange, apiParam); + return getForwardExchange(exchange, newPath); } /** - * 获取Spring Cloud Gateway请求的原始参数。前提是要使用ReadBodyRoutePredicateFactory + * 重定向 * - * @param exchange ServerWebExchange - * @return 没有参数返回null - * @see com.gitee.sop.gatewaycommon.gateway.route.ReadBodyRoutePredicateFactory + * @param exchange exchange + * @param forwardPath 重定向path + * @return 返回新的ServerWebExchange,配合chain.filter(newExchange);使用 */ - public static ApiParam getRequestParams(ServerWebExchange exchange) { - ApiParam apiParamExist = exchange.getAttribute(CACHE_REQUEST_BODY_FOR_MAP); - if (apiParamExist != null) { - return apiParamExist; + public static ServerWebExchange getForwardExchange(ServerWebExchange exchange, String forwardPath) { + ServerHttpRequest newRequest = exchange.getRequest() + .mutate() + .path(forwardPath).build(); + return exchange.mutate().request(newRequest).build(); + } + + public static Mono forwardUnknown(ServerWebExchange exchange, WebFilterChain chain) { + // 非法访问 + ServerWebExchange newExchange = ServerWebExchangeUtil.getForwardExchange(exchange, UNKNOWN_PATH); + return chain.filter(newExchange); + } + + private static String getForwardPath(ApiParam apiParam) { + // 如果有异常,则重定向到这个path + if (apiParam.getThrowable() != null) { + return VALIDATE_ERROR_PATH; + } + String forwardPath = UNKNOWN_PATH; + String method = apiParam.fetchName(); + if (org.springframework.util.StringUtils.hasText(method)) { + forwardPath = "/" + method + "/"; + } + return forwardPath; + } + + public static ApiParam getApiParam(ServerWebExchange exchange, String body) { + MediaType contentType = exchange.getRequest().getHeaders().getContentType(); + if (contentType == null) { + contentType = MediaType.APPLICATION_FORM_URLENCODED; } ApiParam apiParam = new ApiParam(); + String ip = Optional.ofNullable(exchange.getRequest().getRemoteAddress()) + .map(address -> address.getAddress().getHostAddress()) + .orElse(""); + apiParam.setIp(ip); Map params = null; - if (exchange.getRequest().getMethod() == HttpMethod.GET) { - MultiValueMap queryParams = exchange.getRequest().getQueryParams(); - params = buildParams(queryParams); + String contentTypeStr = contentType.toString().toLowerCase(); + // 如果是json方式提交 + if (StringUtils.containsAny(contentTypeStr, "json", "text")) { + JSONObject jsonObject = JSON.parseObject(body); + apiParam.putAll(jsonObject); + } else if (StringUtils.containsIgnoreCase(contentTypeStr, "multipart")) { + // 如果是文件上传请求 + HttpServletRequest fileUploadRequest = getFileUploadRequest(exchange, body); + setFileUploadRequest(exchange, fileUploadRequest); + RequestUtil.UploadInfo uploadInfo = RequestUtil.getUploadInfo(fileUploadRequest); + params = uploadInfo.getUploadParams(); + apiParam.setUploadContext(uploadInfo.getUploadContext()); } else { - String cachedBody = exchange.getAttribute(CACHE_REQUEST_BODY_OBJECT_KEY); - if (cachedBody != null) { - MediaType contentType = exchange.getRequest().getHeaders().getContentType(); - String contentTypeStr = contentType == null ? "" : contentType.toString().toLowerCase(); - // 如果是json方式提交 - if (StringUtils.containsAny(contentTypeStr, "json", "text")) { - params = JSON.parseObject(cachedBody); - } else if (StringUtils.containsIgnoreCase(contentTypeStr, "multipart")) { - // 如果是文件上传请求 - HttpServletRequest fileUploadRequest = getFileUploadRequest(exchange, cachedBody); - setFileUploadRequest(exchange, fileUploadRequest); - RequestUtil.UploadInfo uploadInfo = RequestUtil.getUploadInfo(fileUploadRequest); - params = uploadInfo.getUploadParams(); - apiParam.setUploadContext(uploadInfo.getUploadContext()); - } else { - params = RequestUtil.parseQueryToMap(cachedBody); - } - } + // APPLICATION_FORM_URLENCODED请求 + params = RequestUtil.parseQueryToMap(body); } if (params != null) { apiParam.putAll(params); - exchange.getAttributes().put(CACHE_REQUEST_BODY_FOR_MAP, apiParam); } + setApiParam(exchange, apiParam); return apiParam; } + public static ApiParam getApiParam(ServerWebExchange exchange, Map params) { + ApiParam apiParam = new ApiParam(); + apiParam.putAll(params); + setApiParam(exchange, apiParam); + return apiParam; + } - public static Map buildParams(MultiValueMap queryParams) { - if (queryParams == null || queryParams.size() == 0) { - return null; - } - Map params = new HashMap<>(queryParams.size()); - for (Map.Entry> entry : queryParams.entrySet()) { - params.put(entry.getKey(), entry.getValue().get(0)); - } - return params; + /** + * 获取请求参数 + * + * @param exchange ServerWebExchange + * @return 返回请求参数 + */ + public static ApiParam getApiParam(ServerWebExchange exchange) { + return exchange.getAttribute(SopConstants.CACHE_API_PARAM); + } + + /** + * 设置请求参数 + * + * @param exchange ServerWebExchange + * @param apiParam 请求参数 + */ + public static void setApiParam(ServerWebExchange exchange, ApiParam apiParam) { + exchange.getAttributes().put(SopConstants.CACHE_API_PARAM, apiParam); } /** diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/configuration/BaseGatewayConfiguration.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/configuration/BaseGatewayConfiguration.java index 7df72e6f..d5d2375f 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/configuration/BaseGatewayConfiguration.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/configuration/BaseGatewayConfiguration.java @@ -1,47 +1,29 @@ package com.gitee.sop.gatewaycommon.gateway.configuration; import com.gitee.sop.gatewaycommon.bean.ApiConfig; +import com.gitee.sop.gatewaycommon.gateway.controller.ConfigChannelController; +import com.gitee.sop.gatewaycommon.gateway.controller.ErrorLogController; +import com.gitee.sop.gatewaycommon.gateway.controller.GatewayController; import com.gitee.sop.gatewaycommon.gateway.filter.EnvGrayFilter; import com.gitee.sop.gatewaycommon.gateway.filter.GatewayModifyResponseGatewayFilter; +import com.gitee.sop.gatewaycommon.gateway.filter.IndexFilter; import com.gitee.sop.gatewaycommon.gateway.filter.LimitFilter; -import com.gitee.sop.gatewaycommon.gateway.filter.LoadBalancerClientExtFilter; import com.gitee.sop.gatewaycommon.gateway.filter.ParameterFormatterFilter; -import com.gitee.sop.gatewaycommon.gateway.filter.ValidateFilter; import com.gitee.sop.gatewaycommon.gateway.handler.GatewayExceptionHandler; import com.gitee.sop.gatewaycommon.gateway.route.GatewayRouteCache; import com.gitee.sop.gatewaycommon.gateway.route.GatewayRouteRepository; -import com.gitee.sop.gatewaycommon.gateway.route.NameVersionRoutePredicateFactory; -import com.gitee.sop.gatewaycommon.gateway.route.ReadBodyRoutePredicateFactory; import com.gitee.sop.gatewaycommon.manager.AbstractConfiguration; import com.gitee.sop.gatewaycommon.manager.RouteRepositoryContext; -import com.gitee.sop.gatewaycommon.param.ParamBuilder; -import com.gitee.sop.gatewaycommon.param.ParamNames; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.ObjectProvider; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 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.io.buffer.DataBuffer; -import org.springframework.core.io.buffer.DefaultDataBufferFactory; import org.springframework.http.codec.ServerCodecConfigurer; -import org.springframework.util.MultiValueMap; -import org.springframework.web.reactive.function.BodyInserters; -import org.springframework.web.reactive.function.server.RequestPredicate; -import org.springframework.web.reactive.function.server.RequestPredicates; -import org.springframework.web.reactive.function.server.RouterFunction; -import org.springframework.web.reactive.function.server.RouterFunctions; -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.net.URI; import java.util.Collections; import java.util.List; @@ -56,8 +38,25 @@ public class BaseGatewayConfiguration extends AbstractConfiguration { ApiConfig.getInstance().setUseGateway(true); } - @Value("${sop.restful.path:/rest}") - private String restPath; + @Bean + public IndexFilter indexFilter() { + return new IndexFilter(); + } + + @Bean + public GatewayController gatewayErrorController() { + return new GatewayController(); + } + + @Bean + public ConfigChannelController configChannelController() { + return new ConfigChannelController(); + } + + @Bean + public ErrorLogController errorLogController() { + return new ErrorLogController(); + } /** * 自定义异常处理[@@]注册Bean时依赖的Bean,会从容器中直接获取,所以直接注入即可 @@ -68,7 +67,7 @@ public class BaseGatewayConfiguration extends AbstractConfiguration { @Primary @Bean @Order(Ordered.HIGHEST_PRECEDENCE) - public ErrorWebExceptionHandler errorWebExceptionHandler(ObjectProvider> viewResolversProvider, + public ErrorWebExceptionHandler sopErrorWebExceptionHandler(ObjectProvider> viewResolversProvider, ServerCodecConfigurer serverCodecConfigurer) { GatewayExceptionHandler jsonExceptionHandler = new GatewayExceptionHandler(); @@ -78,12 +77,6 @@ public class BaseGatewayConfiguration extends AbstractConfiguration { return jsonExceptionHandler; } - @Bean - @ConditionalOnMissingBean - ParamBuilder paramBuilder() { - return ApiConfig.getInstance().getGatewayParamBuilder(); - } - /** * 处理返回结果 */ @@ -92,24 +85,6 @@ public class BaseGatewayConfiguration extends AbstractConfiguration { return new GatewayModifyResponseGatewayFilter(); } - /** - * 读取post请求参数 - */ - @Bean - ReadBodyRoutePredicateFactory readBodyRoutePredicateFactory() { - return new ReadBodyRoutePredicateFactory(); - } - - @Bean - NameVersionRoutePredicateFactory paramRoutePredicateFactory() { - return new NameVersionRoutePredicateFactory(); - } - - @Bean - ValidateFilter validateFilter() { - return new ValidateFilter(); - } - @Bean ParameterFormatterFilter parameterFormatterFilter() { return new ParameterFormatterFilter(); @@ -120,11 +95,6 @@ public class BaseGatewayConfiguration extends AbstractConfiguration { return new LimitFilter(); } - @Bean - LoadBalancerClientExtFilter loadBalancerClientExtFilter() { - return new LoadBalancerClientExtFilter(); - } - @Bean GatewayRouteCache gatewayRouteCache(GatewayRouteRepository gatewayRouteRepository) { return new GatewayRouteCache(gatewayRouteRepository); @@ -143,41 +113,4 @@ public class BaseGatewayConfiguration extends AbstractConfiguration { return new EnvGrayFilter(); } - /** - * 307 Temporary Redirect(临时重定向): - *

- * 在这种情况下,请求应该与另一个URI重复,但后续的请求应仍使用原始的URI。 - * 与302相反,当重新发出原始请求时,不允许更改请求方法。 例如,应该使用另一个POST请求来重复POST请求 - *

- * 308 Permanent Redirect (永久重定向): - *

- * 请求和所有将来的请求应该使用另一个URI重复。 - * 307和308重复302和301的行为,但不允许HTTP方法更改。 例如,将表单提交给永久重定向的资源可能会顺利进行。 - *

- * https://www.cnblogs.com/wuguanglin/p/redirect.html - * - * @return - */ - @Bean - @ConditionalOnProperty(value = "sop.restful.enable", havingValue = "true") - RouterFunction routerFunction() { - RequestPredicate requestPredicate = RequestPredicates.all() - .and(RequestPredicates.path(restPath + "/**")); - return RouterFunctions.route(requestPredicate, (serverRequest) -> { - String path = serverRequest.path(); - int index = path.indexOf(restPath); - // 取/rest的后面部分 - String servletPath = path.substring(index + restPath.length()); - String query = serverRequest.uri().getQuery(); - String appendQuery = ParamNames.API_NAME + "=" + servletPath + "&" + ParamNames.VERSION_NAME + "="; - if (StringUtils.isBlank(query)) { - query = appendQuery; - } else { - query += '&' + appendQuery; - } - return ServerResponse - .temporaryRedirect(URI.create("/?" + query)) - .build(); - }); - } } diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/controller/ConfigChannelController.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/controller/ConfigChannelController.java new file mode 100644 index 00000000..1ccc64e1 --- /dev/null +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/controller/ConfigChannelController.java @@ -0,0 +1,75 @@ +package com.gitee.sop.gatewaycommon.gateway.controller; + +import com.alibaba.fastjson.JSON; +import com.gitee.sop.gatewaycommon.bean.GatewayPushDTO; +import com.gitee.sop.gatewaycommon.bean.NacosConfigs; +import com.gitee.sop.gatewaycommon.bean.SpringContext; +import com.gitee.sop.gatewaycommon.gateway.ServerWebExchangeUtil; +import com.gitee.sop.gatewaycommon.manager.ChannelMsgProcessor; +import com.gitee.sop.gatewaycommon.manager.EnvGrayManager; +import com.gitee.sop.gatewaycommon.manager.IPBlacklistManager; +import com.gitee.sop.gatewaycommon.manager.IsvRoutePermissionManager; +import com.gitee.sop.gatewaycommon.manager.LimitConfigManager; +import com.gitee.sop.gatewaycommon.manager.RouteConfigManager; +import com.gitee.sop.gatewaycommon.secret.IsvManager; +import com.gitee.sop.gatewaycommon.util.RequestUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author tanghc + */ +@Slf4j +@RestController +public class ConfigChannelController { + + private static Map> processorMap = new HashMap<>(16); + + static { + processorMap.put(NacosConfigs.GROUP_CHANNEL + NacosConfigs.DATA_ID_GRAY, EnvGrayManager.class); + processorMap.put(NacosConfigs.GROUP_CHANNEL + NacosConfigs.DATA_ID_IP_BLACKLIST, IPBlacklistManager.class); + processorMap.put(NacosConfigs.GROUP_CHANNEL + NacosConfigs.DATA_ID_ISV, IsvManager.class); + processorMap.put(NacosConfigs.GROUP_CHANNEL + NacosConfigs.DATA_ID_ROUTE_PERMISSION, IsvRoutePermissionManager.class); + processorMap.put(NacosConfigs.GROUP_CHANNEL + NacosConfigs.DATA_ID_LIMIT_CONFIG, LimitConfigManager.class); + processorMap.put(NacosConfigs.GROUP_CHANNEL + NacosConfigs.DATA_ID_ROUTE_CONFIG, RouteConfigManager.class); + } + + @Value("${sop.secret}") + private String secret; + + @PostMapping("/sop/configChannelMsg") + public Mono configChannel(ServerWebExchange exchange) { + ServerRequest serverRequest = ServerWebExchangeUtil.createReadBodyRequest(exchange); + // 读取请求体中的内容 + return serverRequest.bodyToMono(String.class) + .flatMap(requestJson -> { + String sign = exchange.getRequest().getHeaders().getFirst("sign"); + try { + // 签名验证 + RequestUtil.checkResponseBody(requestJson, sign, secret); + } catch (Exception e) { + log.error("configChannelMsg错误", e); + return Mono.just(e.getMessage()); + } + GatewayPushDTO gatewayPushDTO = JSON.parseObject(requestJson, GatewayPushDTO.class); + ChannelMsgProcessor channelMsgProcessor = getChannelMsgProcessor(gatewayPushDTO); + channelMsgProcessor.process(gatewayPushDTO.getChannelMsg()); + return Mono.just("ok"); + }); + } + + private ChannelMsgProcessor getChannelMsgProcessor(GatewayPushDTO gatewayPushDTO) { + String key = gatewayPushDTO.getGroupId() + gatewayPushDTO.getDataId(); + Class aClass = processorMap.get(key); + return SpringContext.getBean(aClass); + } + +} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/controller/ErrorLogController.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/controller/ErrorLogController.java new file mode 100644 index 00000000..978e533a --- /dev/null +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/controller/ErrorLogController.java @@ -0,0 +1,21 @@ +package com.gitee.sop.gatewaycommon.gateway.controller; + +import com.gitee.sop.gatewaycommon.bean.BaseErrorLogController; +import com.gitee.sop.gatewaycommon.param.ApiParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.server.ServerWebExchange; + +import java.util.Map; + +/** + * @author tanghc + */ +@RestController +public class ErrorLogController extends BaseErrorLogController { + + @Override + protected ApiParam getApiParam(ServerWebExchange request) { + Map params = request.getRequest().getQueryParams().toSingleValueMap(); + return ApiParam.build(params); + } +} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/controller/GatewayController.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/controller/GatewayController.java new file mode 100644 index 00000000..c67c4a19 --- /dev/null +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/controller/GatewayController.java @@ -0,0 +1,43 @@ +package com.gitee.sop.gatewaycommon.gateway.controller; + +import com.gitee.sop.gatewaycommon.bean.ApiContext; +import com.gitee.sop.gatewaycommon.exception.ApiException; +import com.gitee.sop.gatewaycommon.gateway.ServerWebExchangeUtil; +import com.gitee.sop.gatewaycommon.message.ErrorEnum; +import com.gitee.sop.gatewaycommon.param.ApiParam; +import com.gitee.sop.gatewaycommon.result.ResultExecutor; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +/** + * @author tanghc + */ +@RestController +public class GatewayController { + + /** + * 处理签名错误返回 + * + * @param exchange exchange + * @return 返回最终结果 + */ + @RequestMapping("/sop/validateError") + public Mono validateError(ServerWebExchange exchange) { + ApiParam apiParam = ServerWebExchangeUtil.getApiParam(exchange); + // 合并微服务传递过来的结果,变成最终结果 + ResultExecutor resultExecutor = ApiContext.getApiConfig().getGatewayResultExecutor(); + String gatewayResult = resultExecutor.buildErrorResult(exchange, apiParam.getThrowable()); + return Mono.just(gatewayResult); + } + + @RequestMapping("/sop/unknown") + public Mono unknown(ServerWebExchange exchange) { + ApiException exception = ErrorEnum.ISV_INVALID_METHOD.getErrorMeta().getException(); + ResultExecutor resultExecutor = ApiContext.getApiConfig().getGatewayResultExecutor(); + String gatewayResult = resultExecutor.buildErrorResult(exchange, exception); + return Mono.just(gatewayResult); + } + +} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/EnvGrayFilter.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/EnvGrayFilter.java index 38c9aebe..9c0f9fd0 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/EnvGrayFilter.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/EnvGrayFilter.java @@ -27,7 +27,7 @@ public class EnvGrayFilter implements GlobalFilter, Ordered { String nameVersion = apiParam.fetchNameVersion(); TargetRoute targetRoute = RouteRepositoryContext.getRouteRepository().get(nameVersion); if (targetRoute == null) { - return null; + return chain.filter(exchange); } String serviceId = targetRoute.getServiceRouteInfo().fetchServiceIdLowerCase(); // 如果服务在灰度阶段,返回一个灰度版本号 diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/IndexFilter.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/IndexFilter.java new file mode 100644 index 00000000..9b380231 --- /dev/null +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/IndexFilter.java @@ -0,0 +1,153 @@ +package com.gitee.sop.gatewaycommon.gateway.filter; + +import com.gitee.sop.gatewaycommon.bean.SopConstants; +import com.gitee.sop.gatewaycommon.exception.ApiException; +import com.gitee.sop.gatewaycommon.gateway.ServerWebExchangeUtil; +import com.gitee.sop.gatewaycommon.manager.EnvironmentContext; +import com.gitee.sop.gatewaycommon.manager.EnvironmentKeys; +import com.gitee.sop.gatewaycommon.param.ApiParam; +import com.gitee.sop.gatewaycommon.validate.Validator; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.gateway.support.BodyInserterContext; +import org.springframework.cloud.gateway.support.CachedBodyOutputMessage; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.codec.HttpMessageReader; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpRequestDecorator; +import org.springframework.web.reactive.function.BodyInserter; +import org.springframework.web.reactive.function.BodyInserters; +import org.springframework.web.reactive.function.server.HandlerStrategies; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.WebFilter; +import org.springframework.web.server.WebFilterChain; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.net.URI; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +/** + * 入口 + * + * @author tanghc + */ +@Slf4j +@Order(Ordered.HIGHEST_PRECEDENCE) +public class IndexFilter implements WebFilter { + + private static final String INDEX_PATH = "/"; + private static final String REST_PATH_PREFIX = "/rest"; + private static final String SOP_PATH_PREFIX = "/sop"; + + @Autowired + private Validator validator; + + @Override + public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { + ServerHttpRequest request = exchange.getRequest(); + String path = request.getURI().getPath(); + // SOP路径,直接放行 + if (path.startsWith(SOP_PATH_PREFIX)) { + return chain.filter(exchange); + } + // 如果是restful请求,直接转发 + if (path.startsWith(REST_PATH_PREFIX)) { + String sopRestfulEnableValue = EnvironmentKeys.SOP_RESTFUL_ENABLE.getValue(); + if (!Objects.equals("true", sopRestfulEnableValue)) { + log.error("尝试调用restful请求,但sop.restful.enable未开启"); + return ServerWebExchangeUtil.forwardUnknown(exchange, chain); + } + ServerWebExchange newExchange = ServerWebExchangeUtil.getRestfulExchange(exchange, path); + return chain.filter(newExchange); + } + if (Objects.equals(path, INDEX_PATH)) { + if (request.getMethod() == HttpMethod.POST) { + ServerRequest serverRequest = ServerWebExchangeUtil.createReadBodyRequest(exchange); + // 读取请求体中的内容 + Mono modifiedBody = serverRequest.bodyToMono(String.class) + .flatMap(body -> { + // 构建ApiParam + ApiParam apiParam = ServerWebExchangeUtil.getApiParam(exchange, body); + // 签名验证 + doValidate(apiParam); + return Mono.just(body); + }); + BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, (Class) String.class); + HttpHeaders headers = new HttpHeaders(); + headers.putAll(exchange.getRequest().getHeaders()); + + // the new content type will be computed by bodyInserter + // and then set in the request decorator + headers.remove(HttpHeaders.CONTENT_LENGTH); + + CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage( + exchange, headers); + return bodyInserter.insert(outputMessage, new BodyInserterContext()) + .then(Mono.defer(() -> { + ServerHttpRequest decorator = decorate(exchange, headers, outputMessage); + ServerWebExchange newExchange = exchange.mutate().request(decorator).build(); + return chain.filter(newExchange); + })); + + } else { + URI uri = exchange.getRequest().getURI(); + // 原始参数 + String originalQuery = uri.getRawQuery(); + // 构建ApiParam + ApiParam apiParam = ServerWebExchangeUtil.getApiParam(exchange, originalQuery); + // 签名验证 + doValidate(apiParam); + ServerWebExchange newExchange = ServerWebExchangeUtil.getForwardExchange(exchange, apiParam); + return chain.filter(newExchange); + } + } else { + return ServerWebExchangeUtil.forwardUnknown(exchange, chain); + } + } + + private void doValidate(ApiParam apiParam) { + try { + validator.validate(apiParam); + } catch (ApiException e) { + log.error("验证失败,ip:{}, params:{}, errorMsg:{}", apiParam.fetchIp(), apiParam.toJSONString(), e.getMessage()); + apiParam.setThrowable(e); + } + } + + private ServerHttpRequestDecorator decorate( + ServerWebExchange exchange + , HttpHeaders headers + , CachedBodyOutputMessage outputMessage + ) { + ApiParam apiParam = ServerWebExchangeUtil.getApiParam(exchange); + ServerHttpRequest newRequest = ServerWebExchangeUtil.getForwardRequest(exchange.getRequest(), apiParam); + return new ServerHttpRequestDecorator(newRequest) { + @Override + public HttpHeaders getHeaders() { + long contentLength = headers.getContentLength(); + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.putAll(super.getHeaders()); + if (contentLength > 0) { + httpHeaders.setContentLength(contentLength); + } else { + httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked"); + } + httpHeaders.set(SopConstants.REDIRECT_VERSION_KEY, apiParam.fetchVersion()); + return httpHeaders; + } + + @Override + public Flux getBody() { + return outputMessage.getBody(); + } + }; + } +} \ No newline at end of file diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/LoadBalancerClientExtFilter.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/LoadBalancerClientExtFilter.java deleted file mode 100644 index 8cd9ad21..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/LoadBalancerClientExtFilter.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.gitee.sop.gatewaycommon.gateway.filter; - -import com.gitee.sop.gatewaycommon.bean.SopConstants; -import com.gitee.sop.gatewaycommon.util.RouteUtil; -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; - -/** - * 在LoadBalancerClientFilter后面处理,从Route中找到具体的path,然后插入到uri的path中 - * - * @author tanghc - */ -@Slf4j -public class LoadBalancerClientExtFilter implements GlobalFilter, Ordered { - - @Override - public int getOrder() { - return LOAD_BALANCER_CLIENT_FILTER_ORDER + 1; - } - - @Override - public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { - Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR); - String path = this.findPath(exchange, route); - if (StringUtils.hasLength(path)) { - URI url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR); - UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUri(url); - uriComponentsBuilder.path(path); - URI requestUrl = uriComponentsBuilder.build(true).toUri(); - exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl); - } - return chain.filter(exchange); - } - - protected String findPath(ServerWebExchange exchange, Route route) { - String path = exchange.getAttribute(SopConstants.REDIRECT_PATH_KEY); - if (path != null) { - return path; - } - URI routeUri = route.getUri(); - String uriStr = routeUri.toString(); - return RouteUtil.findPath(uriStr); - } - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/ValidateFilter.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/ValidateFilter.java deleted file mode 100644 index 72c6d582..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/ValidateFilter.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.gitee.sop.gatewaycommon.gateway.filter; - -import com.gitee.sop.gatewaycommon.exception.ApiException; -import com.gitee.sop.gatewaycommon.gateway.ServerWebExchangeUtil; -import com.gitee.sop.gatewaycommon.param.ApiParam; -import com.gitee.sop.gatewaycommon.param.ParamBuilder; -import com.gitee.sop.gatewaycommon.validate.Validator; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -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 { - - @Autowired - private ParamBuilder paramBuilder; - - @Autowired - private Validator validator; - - @Override - public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { - // 解析参数 - ApiParam param = paramBuilder.build(exchange); - ServerWebExchangeUtil.setApiParam(exchange, param); - // 验证操作,这里有负责验证签名参数 - try { - validator.validate(param); - } catch (ApiException e) { - log.error("验证失败,params:{}", param.toJSONString(), e); - throw e; - } - return chain.filter(exchange); - } - - @Override - public int getOrder() { - // 最优先执行 - return Orders.VALIDATE_FILTER_ORDER; - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/handler/GatewayExceptionHandler.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/handler/GatewayExceptionHandler.java index 7d19b212..0f623358 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/handler/GatewayExceptionHandler.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/handler/GatewayExceptionHandler.java @@ -2,15 +2,15 @@ package com.gitee.sop.gatewaycommon.gateway.handler; import com.gitee.sop.gatewaycommon.bean.ApiContext; import com.gitee.sop.gatewaycommon.gateway.ServerWebExchangeUtil; -import com.gitee.sop.gatewaycommon.gateway.result.GatewayResult; import com.gitee.sop.gatewaycommon.param.ApiParam; 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.HttpStatus; +import org.springframework.http.MediaType; 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; @@ -33,10 +33,26 @@ public class GatewayExceptionHandler implements ErrorWebExceptionHandler { private static final Logger log = LoggerFactory.getLogger(GatewayExceptionHandler.class); + + /** + * MessageReader + */ + private List> messageReaders = Collections.emptyList(); + + /** + * MessageWriter + */ + private List> messageWriters = Collections.emptyList(); + + /** + * ViewResolvers + */ + private List viewResolvers = Collections.emptyList(); + @Override public Mono handle(ServerWebExchange exchange, Throwable ex) { - ResultExecutor resultExecutor = ApiContext.getApiConfig().getGatewayResultExecutor(); - GatewayResult errorResult = resultExecutor.buildErrorResult(exchange, ex); + ResultExecutor resultExecutor = ApiContext.getApiConfig().getGatewayResultExecutor(); + String errorResult = resultExecutor.buildErrorResult(exchange, ex); ApiParam apiParam = ServerWebExchangeUtil.getApiParam(exchange); // 错误记录 log.error("gateway网关报错,params:{}, errorMsg:{}", apiParam, ex.getMessage(), ex); @@ -52,21 +68,6 @@ public class GatewayExceptionHandler implements ErrorWebExceptionHandler { } - /** - * MessageReader - */ - private List> messageReaders = Collections.emptyList(); - - /** - * MessageWriter - */ - private List> messageWriters = Collections.emptyList(); - - /** - * ViewResolvers - */ - private List viewResolvers = Collections.emptyList(); - /** * 参考AbstractErrorWebExceptionHandler * @@ -103,11 +104,11 @@ public class GatewayExceptionHandler implements ErrorWebExceptionHandler { * @param result 返回结果 * @return 返回mono */ - protected Mono renderErrorResponse(GatewayResult result) { + protected Mono renderErrorResponse(String result) { return ServerResponse - .status(result.getHttpStatus()) - .contentType(result.getContentType()) - .body(BodyInserters.fromObject(result.getBody())); + .status(HttpStatus.OK) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .body(BodyInserters.fromObject(result)); } /** diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/param/GatewayParamBuilder.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/param/GatewayParamBuilder.java deleted file mode 100644 index a1054f8d..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/param/GatewayParamBuilder.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.gitee.sop.gatewaycommon.gateway.param; - -import com.gitee.sop.gatewaycommon.gateway.ServerWebExchangeUtil; -import com.gitee.sop.gatewaycommon.param.ApiParam; -import com.gitee.sop.gatewaycommon.param.BaseParamBuilder; -import org.springframework.web.server.ServerWebExchange; - -/** - * @author tanghc - */ -public class GatewayParamBuilder extends BaseParamBuilder { - - @Override - public ApiParam buildRequestParams(ServerWebExchange exchange) { - return ServerWebExchangeUtil.getRequestParams(exchange); - } - - @Override - public String getIP(ServerWebExchange ctx) { - return ctx.getRequest().getRemoteAddress().getAddress().getHostAddress(); - } - - @Override - public void setVersionInHeader(ServerWebExchange ctx, String headerName, String version) { - ctx.getRequest().getHeaders().add(headerName, version); - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/result/GatewayResultExecutor.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/result/GatewayResultExecutor.java index 55f57a92..9554647a 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/result/GatewayResultExecutor.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/result/GatewayResultExecutor.java @@ -4,12 +4,13 @@ import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.gitee.sop.gatewaycommon.bean.SopConstants; import com.gitee.sop.gatewaycommon.exception.ApiException; +import com.gitee.sop.gatewaycommon.gateway.ServerWebExchangeUtil; import com.gitee.sop.gatewaycommon.message.Error; import com.gitee.sop.gatewaycommon.message.ErrorEnum; +import com.gitee.sop.gatewaycommon.param.ApiParam; import com.gitee.sop.gatewaycommon.result.BaseExecutorAdapter; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; import org.springframework.util.CollectionUtils; import org.springframework.web.server.ServerWebExchange; @@ -21,7 +22,7 @@ import java.util.Map; * @author tanghc */ @Slf4j -public class GatewayResultExecutor extends BaseExecutorAdapter { +public class GatewayResultExecutor extends BaseExecutorAdapter { @Override public int getResponseStatus(ServerWebExchange exchange) { @@ -46,12 +47,12 @@ public class GatewayResultExecutor extends BaseExecutorAdapter getApiParam(ServerWebExchange exchange) { - return exchange.getAttribute(SopConstants.CACHE_REQUEST_BODY_FOR_MAP); + public ApiParam getApiParam(ServerWebExchange exchange) { + return ServerWebExchangeUtil.getApiParam(exchange); } @Override - public GatewayResult buildErrorResult(ServerWebExchange exchange, Throwable ex) { + public String buildErrorResult(ServerWebExchange exchange, Throwable ex) { Error error; if (ex instanceof ApiException) { ApiException apiException = (ApiException) ex; @@ -59,11 +60,8 @@ public class GatewayResultExecutor extends BaseExecutorAdapter { path = path.replace('{', '?'); path = path.replace('}', '?'); } - targetRoute.setUri(URI.create(routeDefinition.getUri() + "#" + path)); + targetRoute.setUri(URI.create(routeDefinition.getUri() + "/" + path)); targetRoute.setOrder(routeDefinition.getOrder()); // 添加过滤器 List filterDefinitionList = routeDefinition.getFilters() @@ -43,40 +40,7 @@ public class GatewayRouteCache extends BaseRouteCache { return filterDefinition; }) .collect(Collectors.toList()); - - - LinkedList predicateDefinitionList = routeDefinition.getPredicates() - .stream() - .map(predicate -> { - PredicateDefinition predicateDefinition = new PredicateDefinition(); - BeanUtils.copyProperties(predicate, predicateDefinition); - return predicateDefinition; - }) - .collect(Collectors.toCollection(LinkedList::new)); - // 下面两个自定义的断言添加到顶部,注意:ReadBody需要放在最前面 - // 对应断言: - // NameVersion -> com.gitee.sop.gatewaycommon.gateway.route.NameVersionRoutePredicateFactory - // ReadBody -> com.gitee.sop.gatewaycommon.gateway.route.ReadBodyRoutePredicateFactory - predicateDefinitionList.addFirst(new PredicateDefinition("NameVersion=" + routeDefinition.getId())); - predicateDefinitionList.addFirst(new PredicateDefinition("ReadBody=")); - - targetRoute.setFilters(filterDefinitionList); - targetRoute.setPredicates(predicateDefinitionList); return new GatewayTargetRoute(serviceRouteInfo, routeDefinition, targetRoute); } - @Override - protected List getExtRouteDefinitionList(ServiceRouteInfo serviceRouteInfo) { - // 在第一个位置放一个没用的路由,SpringCloudGateway会从第二个路由开始找,原因不详 - RouteDefinition routeDefinition = new RouteDefinition(); - String name = "_first_route_"; - String version = String.valueOf(System.currentTimeMillis()); - routeDefinition.setId(name + version); - routeDefinition.setName(name); - routeDefinition.setVersion(version); - routeDefinition.setUri("lb://" + serviceRouteInfo.getServiceId()); - routeDefinition.setOrder(Integer.MIN_VALUE); - - return Collections.singletonList(routeDefinition); - } } diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/GatewayRouteRepository.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/GatewayRouteRepository.java index 2a54578c..fa8c0f54 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/GatewayRouteRepository.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/GatewayRouteRepository.java @@ -1,15 +1,19 @@ package com.gitee.sop.gatewaycommon.gateway.route; +import com.gitee.sop.gatewaycommon.bean.AbstractTargetRoute; import com.gitee.sop.gatewaycommon.bean.RouteDefinition; -import com.gitee.sop.gatewaycommon.bean.TargetRoute; import com.gitee.sop.gatewaycommon.manager.RouteRepository; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; -import org.springframework.cloud.gateway.route.RouteDefinitionRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.gateway.event.RefreshRoutesEvent; +import org.springframework.cloud.gateway.route.Route; +import org.springframework.cloud.gateway.route.RouteLocator; +import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder; +import org.springframework.context.ApplicationContext; import org.springframework.util.AntPathMatcher; import org.springframework.util.PathMatcher; import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; import java.util.Collection; import java.util.LinkedHashMap; @@ -25,28 +29,37 @@ import static java.util.Collections.synchronizedMap; * @author tanghc */ @Slf4j -public class GatewayRouteRepository implements RouteDefinitionRepository, RouteRepository { +public class GatewayRouteRepository implements RouteRepository, RouteLocator { private PathMatcher pathMatcher = new AntPathMatcher(); private final Map routes = synchronizedMap(new LinkedHashMap<>()); - @Override - public Flux getRouteDefinitions() { - List list = routes.values().parallelStream() - .map(TargetRoute::getTargetRouteDefinition) - .collect(Collectors.toList()); - return Flux.fromIterable(list); - } + @Autowired + private RouteLocatorBuilder routeLocatorBuilder; + + @Autowired + private ApplicationContext applicationContext; + + private RouteLocator routeLocator; @Override - public Mono save(Mono route) { - return null; + public Flux getRoutes() { + return routeLocator.getRoutes(); } - @Override - public Mono delete(Mono routeId) { - return null; + public void refresh() { + RouteLocatorBuilder.Builder builder = routeLocatorBuilder.routes(); + List routeDefinitionList = this.routes.values() + .stream() + .map(AbstractTargetRoute::getRouteDefinition) + .collect(Collectors.toList()); + routeDefinitionList.forEach(routeDefinition -> builder.route(routeDefinition.getId(), + r -> r.path(routeDefinition.getPath()) + .uri(routeDefinition.getUri()))); + this.routeLocator = builder.build(); + // 触发 + applicationContext.publishEvent(new RefreshRoutesEvent(new Object())); } /** diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/NameVersionRoutePredicateFactory.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/NameVersionRoutePredicateFactory.java deleted file mode 100644 index a3a61203..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/NameVersionRoutePredicateFactory.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.gitee.sop.gatewaycommon.gateway.route; - -import com.gitee.sop.gatewaycommon.bean.SopConstants; -import com.gitee.sop.gatewaycommon.gateway.ServerWebExchangeUtil; -import com.gitee.sop.gatewaycommon.param.ParamNames; -import lombok.extern.slf4j.Slf4j; -import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory; -import org.springframework.util.AntPathMatcher; -import org.springframework.util.CollectionUtils; -import org.springframework.util.PathMatcher; -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; - -/** - * 此断言决定执行哪个路由 - * - * 使用地方: - * @see com.gitee.sop.gatewaycommon.gateway.route.GatewayRouteCache - * - * @author tanghc - */ -@Slf4j -public class NameVersionRoutePredicateFactory extends AbstractRoutePredicateFactory { - - private static final String PARAM_KEY = "param"; - private static final String REGEXP_KEY = "regexp"; - - private PathMatcher pathMatcher = new AntPathMatcher(); - - public NameVersionRoutePredicateFactory() { - super(Config.class); - } - - @Override - public List shortcutFieldOrder() { - return Arrays.asList(PARAM_KEY, REGEXP_KEY); - } - - /** - * config.param为nameVersion,即路由id - * - * @param config - * @return 返回断言 - */ - @Override - public Predicate apply(Config config) { - - return exchange -> { - Map params = ServerWebExchangeUtil.getRequestParams(exchange); - if (CollectionUtils.isEmpty(params)) { - return false; - } - String nameVersion = config.param; - Object name = params.get(ParamNames.API_NAME); - Object version = params.get(ParamNames.VERSION_NAME); - if (name == null || version == null) { - return false; - } - String key = name.toString() + version.toString(); - // 如果是通配符 - if (nameVersion.contains("{")) { - boolean match = pathMatcher.match(nameVersion, key); - if (match) { - Map attributes = exchange.getAttributes(); - attributes.put(SopConstants.REDIRECT_PATH_KEY, key); - } - return match; - } else { - return key.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; - } - } -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/ReadBodyRoutePredicateFactory.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/ReadBodyRoutePredicateFactory.java deleted file mode 100644 index d8f48b1c..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/ReadBodyRoutePredicateFactory.java +++ /dev/null @@ -1,202 +0,0 @@ -package com.gitee.sop.gatewaycommon.gateway.route; - -import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.cloud.gateway.handler.AsyncPredicate; -import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory; -import org.springframework.cloud.gateway.handler.predicate.ReadBodyPredicateFactory; -import org.springframework.core.io.buffer.DataBuffer; -import org.springframework.core.io.buffer.DataBufferUtils; -import org.springframework.http.HttpMethod; -import org.springframework.http.codec.HttpMessageReader; -import org.springframework.http.server.reactive.ServerHttpRequest; -import org.springframework.http.server.reactive.ServerHttpRequestDecorator; -import org.springframework.util.MultiValueMap; -import org.springframework.web.reactive.function.server.HandlerStrategies; -import org.springframework.web.reactive.function.server.ServerRequest; -import org.springframework.web.server.ServerWebExchange; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.function.Predicate; - -import static org.springframework.cloud.gateway.filter.AdaptCachedBodyGlobalFilter.CACHED_REQUEST_BODY_KEY; - -/** - * 获取请求参数插件,兼容get,post,使用方式: - * @Bean - * ReadBodyRoutePredicateFactory readBodyRoutePredicateFactory() { - * return new ReadBodyRoutePredicateFactory(); - * } - * - * @see org.springframework.cloud.gateway.handler.predicate.ReadBodyPredicateFactory - * 详见:https://blog.51cto.com/thinklili/2329184 - * - * 使用地方: - * @see com.gitee.sop.gatewaycommon.gateway.route.GatewayRouteCache - * - * @author tanghc - */ -public class ReadBodyRoutePredicateFactory extends AbstractRoutePredicateFactory { - - protected static final Log LOGGER = LogFactory.getLog(ReadBodyPredicateFactory.class); - - private static final String TEST_ATTRIBUTE = "read_body_predicate_test_attribute"; - private static final String CACHE_REQUEST_BODY_OBJECT_KEY = "cachedRequestBodyObject"; - private static final List> HTTP_MESSAGE_READERS = HandlerStrategies.withDefaults().messageReaders(); - - - public ReadBodyRoutePredicateFactory() { - super(ReadBodyRoutePredicateFactory.Config.class); - } - - @Override - public AsyncPredicate applyAsync(Config config) { - return exchange -> { - HttpMethod method = exchange.getRequest().getMethod(); - if (method == HttpMethod.POST) { - return this.applyForPost(exchange, config); - } else { - return this.applyForGet(exchange, config); - } - }; - } - - /** - * 获取post表单参数 - * @param exchange - * @param config - * @return - */ - protected Mono applyForPost(ServerWebExchange exchange, Config config) { - Class inClass = config.getInClass(); - - Object cachedBody = exchange.getAttribute(CACHE_REQUEST_BODY_OBJECT_KEY); - // We can only read the body from the request once, once that happens if we try to read the body again an - // exception will be thrown. The below if/else caches the body object as a request attribute in the ServerWebExchange - // so if this filter is run more than once (due to more than one route using it) we do not try to read the - // request body multiple times - if (cachedBody != null) { - try { - boolean test = config.getPredicate().test(cachedBody); - exchange.getAttributes().put(TEST_ATTRIBUTE, test); - return Mono.just(test); - } catch (ClassCastException e) { - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Predicate test failed because class in predicate does not canVisit the cached body object", - e); - } - } - return Mono.just(false); - } else { - //Join all the DataBuffers so we have a single DataBuffer for the body - return DataBufferUtils.join(exchange.getRequest().getBody()) - .flatMap(dataBuffer -> { - //Update the retain counts so we can read the body twice, once to parse into an object - //that we can test the predicate against and a second time when the HTTP client sends - //the request downstream - //Note: if we end up reading the body twice we will run into a problem, but as of right - //now there is no good use case for doing this - DataBufferUtils.retain(dataBuffer); - //Make a slice for each read so each read has its own read/write indexes - Flux cachedFlux = Flux.defer(() -> Flux.just(dataBuffer.slice(0, dataBuffer.readableByteCount()))); - - ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(exchange.getRequest()) { - @Override - public Flux getBody() { - return cachedFlux; - } - }; - return ServerRequest.create(exchange.mutate().request(mutatedRequest).build(), HTTP_MESSAGE_READERS) - .bodyToMono(inClass) - .doOnNext(objectValue -> { - exchange.getAttributes().put(CACHE_REQUEST_BODY_OBJECT_KEY, objectValue); - exchange.getAttributes().put(CACHED_REQUEST_BODY_KEY, cachedFlux); - }) - .map(objectValue -> config.getPredicate().test(objectValue)); - }); - - } - } - - /** - * 获取GET请求参数 - * @param exchange - * @param config - * @return - */ - protected Mono applyForGet(ServerWebExchange exchange, Config config) { - // 处理get请求 - MultiValueMap queryParams = exchange.getRequest().getQueryParams(); - String objectValue = null; - if (queryParams != null && queryParams.size() > 0) { - List params = new ArrayList<>(queryParams.size()); - for (Map.Entry> entry : queryParams.entrySet()) { - params.add(entry.getKey() + "=" + entry.getValue().get(0)); - } - objectValue = StringUtils.join(params.toArray()); - exchange.getAttributes().put(CACHE_REQUEST_BODY_OBJECT_KEY, objectValue); - } - return Mono.just(config.getPredicate().test(objectValue)); - } - - @Override - public Config newConfig() { - Config config = super.newConfig(); - config.setInClass(String.class); - config.setPredicate(Objects::nonNull); - return config; - } - - @Override - @SuppressWarnings("unchecked") - public Predicate apply(Config config) { - throw new UnsupportedOperationException( - "ReadBodyPredicateFactory is only async."); - } - - public static class Config { - private Class inClass; - private Predicate predicate; - private Map hints; - - public Class getInClass() { - return inClass; - } - - public Config setInClass(Class inClass) { - this.inClass = inClass; - return this; - } - - public Predicate getPredicate() { - return predicate; - } - - public Config setPredicate(Class inClass, Predicate predicate) { - setInClass(inClass); - this.predicate = predicate; - return this; - } - - public Config setPredicate(Predicate predicate) { - this.predicate = predicate; - return this; - } - - public Map getHints() { - return hints; - } - - public Config setHints(Map hints) { - this.hints = hints; - return this; - } - } - -} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/BaseRouteCache.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/BaseRouteCache.java index e2dbddd1..0c0ace23 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/BaseRouteCache.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/BaseRouteCache.java @@ -5,12 +5,9 @@ import com.gitee.sop.gatewaycommon.bean.RouteDefinition; import com.gitee.sop.gatewaycommon.bean.ServiceRouteInfo; import com.gitee.sop.gatewaycommon.bean.TargetRoute; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.collections.CollectionUtils; import org.springframework.util.DigestUtils; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -55,21 +52,15 @@ public abstract class BaseRouteCache implements RouteLoad } serviceIdMd5Map.put(serviceId, newMd5); - List extRouteDefinitionList = this.getExtRouteDefinitionList(serviceRouteInfo); List routeDefinitionList = serviceRouteInfo.getRouteDefinitionList(); - int size = extRouteDefinitionList.size() + routeDefinitionList.size(); - List allRoutes = new ArrayList<>(size); - if (CollectionUtils.isNotEmpty(extRouteDefinitionList)) { - allRoutes.addAll(extRouteDefinitionList); - } - allRoutes.addAll(routeDefinitionList); - for (RouteDefinition routeDefinition : allRoutes) { + for (RouteDefinition routeDefinition : routeDefinitionList) { T targetRoute = this.buildTargetRoute(serviceRouteInfo, routeDefinition); routeRepository.add(targetRoute); if (log.isDebugEnabled()) { log.debug("新增路由:{}", JSON.toJSONString(routeDefinition)); } } + this.routeRepository.refresh(); callback.accept(null); } catch (Exception e) { log.error("加载路由信息失败,serviceRouteInfo:{}", serviceRouteInfo, e); @@ -91,14 +82,6 @@ public abstract class BaseRouteCache implements RouteLoad return DigestUtils.md5DigestAsHex(md5Source.getBytes(StandardCharsets.UTF_8)); } - /** - * 返回附加的路由选项 - * @return 返回附加的路由选项 - */ - protected List getExtRouteDefinitionList(ServiceRouteInfo serviceRouteInfo) { - return Collections.emptyList(); - } - @Override public void remove(String serviceId) { serviceIdMd5Map.remove(serviceId); diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/RouteRepository.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/RouteRepository.java index 8976090f..b6435e07 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/RouteRepository.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/RouteRepository.java @@ -45,4 +45,9 @@ public interface RouteRepository { * @param serviceId 服务id */ void deleteAll(String serviceId); + + /** + * 刷新 + */ + default void refresh() {} } diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/RouteRepositoryContext.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/RouteRepositoryContext.java index f103f55e..ec4eb090 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/RouteRepositoryContext.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/RouteRepositoryContext.java @@ -25,12 +25,14 @@ public class RouteRepositoryContext { * 检查路由是否存在,不存在报错 * @param routeId 路由id * @param errorEnum 报错信息 + * @return 返回TargetRoute */ - public static void checkExist(String routeId, ErrorEnum errorEnum) { + public static TargetRoute checkExist(String routeId, ErrorEnum errorEnum) { TargetRoute targetRoute = routeRepository.get(routeId); if (targetRoute == null) { throw errorEnum.getErrorMeta().getException(); } + return targetRoute; } } diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/param/ApiParam.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/param/ApiParam.java index ea5e43bd..e0a38f81 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/param/ApiParam.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/param/ApiParam.java @@ -14,6 +14,8 @@ import java.util.Map; */ public class ApiParam extends JSONObject implements Param { + private static final String THROWABLE_KEY = "Throwable"; + public ApiParam() { } @@ -31,6 +33,14 @@ public class ApiParam extends JSONObject implements Param { private transient UploadContext uploadContext; + public void setThrowable(Throwable throwable) { + this.put(THROWABLE_KEY, throwable); + } + + public Throwable getThrowable() { + return (Throwable)get(THROWABLE_KEY); + } + public void fitNameVersion() { if (restName != null) { this.put(ParamNames.API_NAME, restName); diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/BaseExecutorAdapter.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/BaseExecutorAdapter.java index c986bd2f..7361b758 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/BaseExecutorAdapter.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/BaseExecutorAdapter.java @@ -5,14 +5,15 @@ import com.alibaba.fastjson.JSONObject; import com.gitee.sop.gatewaycommon.bean.ApiConfig; import com.gitee.sop.gatewaycommon.bean.ApiContext; import com.gitee.sop.gatewaycommon.bean.ErrorDefinition; -import com.gitee.sop.gatewaycommon.bean.RouteDefinition; import com.gitee.sop.gatewaycommon.bean.Isv; +import com.gitee.sop.gatewaycommon.bean.RouteDefinition; import com.gitee.sop.gatewaycommon.bean.ServiceRouteInfo; import com.gitee.sop.gatewaycommon.bean.SopConstants; import com.gitee.sop.gatewaycommon.bean.TargetRoute; import com.gitee.sop.gatewaycommon.manager.RouteRepositoryContext; import com.gitee.sop.gatewaycommon.message.ErrorEnum; import com.gitee.sop.gatewaycommon.message.ErrorMeta; +import com.gitee.sop.gatewaycommon.param.ApiParam; import com.gitee.sop.gatewaycommon.param.ParamNames; import com.gitee.sop.gatewaycommon.secret.IsvManager; import com.gitee.sop.gatewaycommon.validate.alipay.AlipayConstants; @@ -69,7 +70,7 @@ public abstract class BaseExecutorAdapter implements ResultExecutor * @param t request * @return 返回api参数 */ - public abstract Map getApiParam(T t); + public abstract ApiParam getApiParam(T t); @Override public String mergeResult(T request, String serviceResult) { @@ -91,12 +92,12 @@ public abstract class BaseExecutorAdapter implements ResultExecutor responseData = JSON.parseObject(serviceResult); responseData.put(GATEWAY_CODE_NAME, ISP_BIZ_ERROR.getCode()); responseData.put(GATEWAY_MSG_NAME, ISP_BIZ_ERROR.getError().getMsg()); - } else if(responseStatus == HttpStatus.NOT_FOUND.value()) { + } else if (responseStatus == HttpStatus.NOT_FOUND.value()) { responseData = JSON.parseObject(serviceResult); responseData.put(GATEWAY_CODE_NAME, ISV_MISSING_METHOD_META.getCode()); responseData.put(GATEWAY_MSG_NAME, ISV_MISSING_METHOD_META.getError().getCode()); } else { - Map params = this.getApiParam(request); + ApiParam params = this.getApiParam(request); log.error("微服务端报错,params:{}, 微服务返回结果:{}", params, serviceResult); this.storeError(request, ErrorType.UNKNOWN); // 微服务端有可能返回500错误 @@ -140,8 +141,11 @@ public abstract class BaseExecutorAdapter implements ResultExecutor if (defaultSetting != null) { return defaultSetting; } - ApiInfo apiInfo = this.getApiInfo(request); - RouteDefinition baseRouteDefinition = apiInfo.gatewayRouteDefinition; + ApiParam params = this.getApiParam(request); + TargetRoute targetRoute = RouteRepositoryContext.getRouteRepository().get(params.fetchNameVersion()); + RouteDefinition baseRouteDefinition = Optional.ofNullable(targetRoute) + .map(TargetRoute::getRouteDefinition) + .orElse(null); return Optional.ofNullable(baseRouteDefinition) .map(routeDefinition -> { int mergeResult = baseRouteDefinition.getMergeResult(); @@ -151,11 +155,8 @@ public abstract class BaseExecutorAdapter implements ResultExecutor } protected ApiInfo getApiInfo(T request) { - Map params = this.getApiParam(request); - String name = this.getParamValue(params, ParamNames.API_NAME, SopConstants.UNKNOWN_METHOD); - String version = this.getParamValue(params, ParamNames.VERSION_NAME, SopConstants.UNKNOWN_VERSION); - - TargetRoute targetRoute = RouteRepositoryContext.getRouteRepository().get(name + version); + ApiParam params = this.getApiParam(request); + TargetRoute targetRoute = RouteRepositoryContext.getRouteRepository().get(params.fetchNameVersion()); String serviceId = Optional.ofNullable(targetRoute) .flatMap(route -> Optional.ofNullable(route.getServiceRouteInfo())) @@ -167,8 +168,8 @@ public abstract class BaseExecutorAdapter implements ResultExecutor .orElse(null); ApiInfo apiInfo = new ApiInfo(); - apiInfo.name = name; - apiInfo.version = version; + apiInfo.name = params.fetchName(); + apiInfo.version = params.fetchVersion(); apiInfo.serviceId = serviceId; apiInfo.gatewayRouteDefinition = baseRouteDefinition; return apiInfo; @@ -190,8 +191,8 @@ public abstract class BaseExecutorAdapter implements ResultExecutor public String merge(T exchange, JSONObject responseData) { JSONObject finalData = new JSONObject(true); - Map params = this.getApiParam(exchange); - String name = this.getParamValue(params, ParamNames.API_NAME, ERROR_METHOD); + ApiParam params = this.getApiParam(exchange); + String name = params.fetchName(); ApiConfig apiConfig = ApiConfig.getInstance(); // 点换成下划线 DataNameBuilder dataNameBuilder = apiConfig.getDataNameBuilder(); diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/ApiValidator.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/ApiValidator.java index 8bdf8f24..bfca1c63 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/ApiValidator.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/ApiValidator.java @@ -68,9 +68,10 @@ public class ApiValidator implements Validator { @Override public void validate(ApiParam param) { checkIP(param); - checkEnable(param); + TargetRoute targetRoute = checkEnable(param); ApiConfig apiConfig = ApiContext.getApiConfig(); - if (apiConfig.isIgnoreValidate() || param.fetchIgnoreValidate()) { + if (apiConfig.isIgnoreValidate() + || BooleanUtils.toBoolean(targetRoute.getRouteDefinition().getIgnoreValidate())) { if (log.isDebugEnabled()) { log.debug("忽略所有验证(ignoreValidate=true), name:{}, version:{}", param.fetchName(), param.fetchVersion()); } @@ -103,7 +104,7 @@ public class ApiValidator implements Validator { * * @param param 接口参数 */ - protected void checkEnable(ApiParam param) { + protected TargetRoute checkEnable(ApiParam param) { String name = param.fetchName(); if (name == null) { throw ErrorEnum.ISV_MISSING_METHOD.getErrorMeta().getException(); @@ -114,12 +115,13 @@ public class ApiValidator implements Validator { } String routeId = param.fetchNameVersion(); // 检查路由是否存在 - RouteRepositoryContext.checkExist(routeId, ErrorEnum.ISV_INVALID_METHOD); + TargetRoute targetRoute = RouteRepositoryContext.checkExist(routeId, ErrorEnum.ISV_INVALID_METHOD); // 检查路由是否启用 RouteConfig routeConfig = routeConfigManager.get(routeId); if (!routeConfig.enable()) { throw ErrorEnum.ISP_API_DISABLED.getErrorMeta().getException(); } + return targetRoute; } /** diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/configuration/BaseZuulConfiguration.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/configuration/BaseZuulConfiguration.java index 3b6dbc9f..7156450b 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/configuration/BaseZuulConfiguration.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/configuration/BaseZuulConfiguration.java @@ -169,8 +169,7 @@ public class BaseZuulConfiguration extends AbstractConfiguration { * 统一错误处理 */ @Bean - @ConditionalOnMissingBean - ZuulErrorController baseZuulController() { + ZuulErrorController sopZuulController() { return ApiContext.getApiConfig().getZuulErrorController(); } diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/configuration/ZuulErrorController.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/configuration/ZuulErrorController.java index cb20a6ae..dff2a294 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/configuration/ZuulErrorController.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/configuration/ZuulErrorController.java @@ -5,10 +5,8 @@ import com.gitee.sop.gatewaycommon.result.ResultExecutor; import com.gitee.sop.gatewaycommon.zuul.ZuulContext; import com.netflix.zuul.context.RequestContext; import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.web.servlet.error.ErrorController; -import org.springframework.http.HttpStatus; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import javax.servlet.http.HttpServletRequest; @@ -19,16 +17,14 @@ import javax.servlet.http.HttpServletResponse; * * @author tanghc */ -@Controller +@ControllerAdvice @Slf4j -public class ZuulErrorController implements ErrorController { - - private static final String ERROR_PATH = "/error"; +public class ZuulErrorController { /** * 错误最终会到这里来 */ - @RequestMapping(ERROR_PATH) + @ExceptionHandler(Exception.class) @ResponseBody public Object error(HttpServletRequest request, HttpServletResponse response) { RequestContext ctx = RequestContext.getCurrentContext(); @@ -49,8 +45,4 @@ public class ZuulErrorController implements ErrorController { return resultExecutor.buildErrorResult(RequestContext.getCurrentContext(), throwable); } - @Override - public String getErrorPath() { - return ERROR_PATH; - } } diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/controller/ConfigChannelController.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/controller/ConfigChannelController.java index 2f1acd07..8cc23726 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/controller/ConfigChannelController.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/controller/ConfigChannelController.java @@ -16,6 +16,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; import java.io.IOException; @@ -26,7 +27,7 @@ import java.util.Map; * @author tanghc */ @Slf4j -@Controller +@RestController public class ConfigChannelController { private static Map> processorMap = new HashMap<>(16); @@ -40,7 +41,7 @@ public class ConfigChannelController { processorMap.put(NacosConfigs.GROUP_CHANNEL + NacosConfigs.DATA_ID_ROUTE_CONFIG, RouteConfigManager.class); } - @Value("${zuul.secret}") + @Value("${sop.secret}") private String secret; @PostMapping("/sop/configChannelMsg") diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/controller/ErrorLogController.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/controller/ErrorLogController.java index 3c773afb..c4907461 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/controller/ErrorLogController.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/controller/ErrorLogController.java @@ -1,72 +1,23 @@ package com.gitee.sop.gatewaycommon.zuul.controller; -import com.gitee.sop.gatewaycommon.bean.ApiConfig; -import com.gitee.sop.gatewaycommon.bean.ErrorEntity; -import com.gitee.sop.gatewaycommon.manager.ServiceErrorManager; +import com.gitee.sop.gatewaycommon.bean.BaseErrorLogController; import com.gitee.sop.gatewaycommon.param.ApiParam; -import com.gitee.sop.gatewaycommon.result.ApiResult; -import com.gitee.sop.gatewaycommon.result.JsonResult; import com.gitee.sop.gatewaycommon.util.RequestUtil; -import com.gitee.sop.gatewaycommon.validate.taobao.TaobaoSigner; -import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; -import java.util.Collection; import java.util.Map; /** * @author tanghc */ -@Controller -public class ErrorLogController { +@RestController +public class ErrorLogController extends BaseErrorLogController { - TaobaoSigner signer = new TaobaoSigner(); - - @Value("${zuul.secret}") - private String secret; - - @GetMapping("listErrors") - public ApiResult listErrors(HttpServletRequest request) { - try { - this.check(request); - ServiceErrorManager serviceErrorManager = ApiConfig.getInstance().getServiceErrorManager(); - Collection allErrors = serviceErrorManager.listAllErrors(); - JsonResult apiResult = new JsonResult(); - apiResult.setData(allErrors); - return apiResult; - } catch (Exception e) { - ApiResult apiResult = new ApiResult(); - apiResult.setCode("505050"); - apiResult.setMsg(e.getMessage()); - return apiResult; - } - } - - @GetMapping("clearErrors") - public ApiResult clearErrors(HttpServletRequest request) { - try { - this.check(request); - ServiceErrorManager serviceErrorManager = ApiConfig.getInstance().getServiceErrorManager(); - serviceErrorManager.clear(); - return new ApiResult(); - } catch (Exception e) { - ApiResult apiResult = new ApiResult(); - apiResult.setCode("505050"); - apiResult.setMsg(e.getMessage()); - return apiResult; - } - } - - private void check(HttpServletRequest request) { + @Override + protected ApiParam getApiParam(HttpServletRequest request) { Map params = RequestUtil.convertRequestParamsToMap(request); - ApiParam apiParam = ApiParam.build(params); - boolean right = signer.checkSign(apiParam, secret); - if (!right) { - throw new RuntimeException("签名校验失败"); - } + return ApiParam.build(params); } - } diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/controller/RestServlet.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/controller/RestServlet.java deleted file mode 100644 index 1a1a484e..00000000 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/controller/RestServlet.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.gitee.sop.gatewaycommon.zuul.controller; - -import com.gitee.sop.gatewaycommon.bean.SopConstants; -import org.springframework.beans.factory.annotation.Value; - -import javax.servlet.ServletException; -import javax.servlet.annotation.WebServlet; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - -/** - * 传统web开发入口 - * @author tanghc - */ -//@WebServlet(urlPatterns = "/rest/*") -public class RestServlet extends HttpServlet { - - private static final String EMPTY_VERSION = ""; - - @Value("${zuul.servlet-path:/zuul}") - private String path; - - @Value("${sop.restful.path:/rest}") - private String restPath; - - @Override - protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - doPost(request, response); - } - - @Override - protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - String url = request.getRequestURL().toString(); - int index = url.indexOf(restPath); - // 取/rest的后面部分 - String path = url.substring(index + restPath.length()); - request.setAttribute(SopConstants.REDIRECT_METHOD_KEY, path); - request.setAttribute(SopConstants.REDIRECT_VERSION_KEY, EMPTY_VERSION); - request.setAttribute(SopConstants.SOP_NOT_MERGE, true); - request.getRequestDispatcher(this.path).forward(request, response); - } - -} \ No newline at end of file diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/result/ZuulResultExecutor.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/result/ZuulResultExecutor.java index 9cfd0cd7..fb428811 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/result/ZuulResultExecutor.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/result/ZuulResultExecutor.java @@ -6,6 +6,7 @@ import com.gitee.sop.gatewaycommon.bean.SopConstants; import com.gitee.sop.gatewaycommon.exception.ApiException; import com.gitee.sop.gatewaycommon.message.Error; import com.gitee.sop.gatewaycommon.message.ErrorEnum; +import com.gitee.sop.gatewaycommon.param.ApiParam; import com.gitee.sop.gatewaycommon.result.BaseExecutorAdapter; import com.gitee.sop.gatewaycommon.zuul.ZuulContext; import com.netflix.util.Pair; @@ -14,7 +15,6 @@ import com.netflix.zuul.exception.ZuulException; import lombok.extern.slf4j.Slf4j; import java.util.List; -import java.util.Map; /** * @author tanghc @@ -62,7 +62,7 @@ public class ZuulResultExecutor extends BaseExecutorAdapter getApiParam(RequestContext requestContext) { + public ApiParam getApiParam(RequestContext requestContext) { return ZuulContext.getApiParam(); } diff --git a/sop-common/sop-gateway-common/src/main/resources/META-INF/spring.factories b/sop-common/sop-gateway-common/src/main/resources/META-INF/spring.factories new file mode 100644 index 00000000..c0f0b43f --- /dev/null +++ b/sop-common/sop-gateway-common/src/main/resources/META-INF/spring.factories @@ -0,0 +1,3 @@ +org.springframework.boot.env.EnvironmentPostProcessor=com.gitee.sop.gatewaycommon.env.SopEnvironmentPostProcessor +# 自定义自动配置类,如果有多个类,使用逗号(,)分隔,\正斜杠标示换行还可以读取到指定的类 +org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.gitee.sop.bridge.SopGatewayAutoConfiguration diff --git a/sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/controller/AlipayController.java b/sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/controller/AlipayController.java index b0530de1..143e71a1 100644 --- a/sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/controller/AlipayController.java +++ b/sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/controller/AlipayController.java @@ -17,6 +17,8 @@ import io.swagger.annotations.ApiOperation; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -39,6 +41,9 @@ import java.util.List; @Api(tags = "故事接口") public class AlipayController { + @Autowired + private Environment environment; + // http://localhost:2222/story_get // 原生的接口,可正常调用 @RequestMapping("story_get") @@ -180,7 +185,7 @@ public class AlipayController { public StoryResult getStory(StoryParam param) { StoryResult story = new StoryResult(); story.setId(1L); - story.setName("海底小纵队(alipay.story.get1.0), param:" + param); + story.setName("海底小纵队(alipay.story.get1.0), port:" + environment.getProperty("server.port") + ", param:" + param); return story; } diff --git a/sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/controller/FileUploadDemoController.java b/sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/controller/FileUploadDemoController.java index ff57a354..e5854db1 100644 --- a/sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/controller/FileUploadDemoController.java +++ b/sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/controller/FileUploadDemoController.java @@ -34,7 +34,6 @@ public class FileUploadDemoController { @ApiOperation(value = "文件上传例1", notes = "上传文件demo") @ApiMapping(value = "demo.file.upload") public FileUploadResult file1(FileUploadParam param) { - System.out.println(param.getRemark()); // 获取上传的文件 MultipartFile file1 = param.getFile1(); MultipartFile file2 = param.getFile2(); @@ -43,6 +42,7 @@ public class FileUploadDemoController { FileUploadResult.FileMeta fileMeta1 = buildFileMeta(file1); FileUploadResult.FileMeta fileMeta2 = buildFileMeta(file2); + result.setRemark(param.getRemark()); result.getFiles().add(fileMeta1); result.getFiles().add(fileMeta2); return result; diff --git a/sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/controller/result/FileUploadResult.java b/sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/controller/result/FileUploadResult.java index 447292ad..11f20176 100644 --- a/sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/controller/result/FileUploadResult.java +++ b/sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/controller/result/FileUploadResult.java @@ -13,6 +13,7 @@ import java.util.List; public class FileUploadResult { private List files = new ArrayList(); + private String remark; @Data public static class FileMeta { diff --git a/sop-gateway/pom.xml b/sop-gateway/pom.xml index 79abd906..8352f1d3 100644 --- a/sop-gateway/pom.xml +++ b/sop-gateway/pom.xml @@ -26,23 +26,15 @@ - + com.gitee.sop - sop-gateway-common + + + + sop-bridge-gateway 2.5.10-SNAPSHOT - - - org.springframework.cloud - spring-cloud-starter-netflix-zuul - - - org.springframework.retry - spring-retry - - - net.oschina.durcframework fastmybatis-spring-boot-starter @@ -70,17 +62,6 @@ - - - com.alibaba.boot - nacos-config-spring-boot-starter - ${nacos-spring-boot-starter.version} - - - org.springframework.boot diff --git a/sop-gateway/src/main/java/com/gitee/sop/gateway/SopGatewayApplication.java b/sop-gateway/src/main/java/com/gitee/sop/gateway/SopGatewayApplication.java index 8b3fe901..20b6880a 100644 --- a/sop-gateway/src/main/java/com/gitee/sop/gateway/SopGatewayApplication.java +++ b/sop-gateway/src/main/java/com/gitee/sop/gateway/SopGatewayApplication.java @@ -2,13 +2,7 @@ package com.gitee.sop.gateway; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.web.servlet.ServletComponentScan; -import org.springframework.cloud.netflix.zuul.EnableZuulProxy; -// 开启网关功能 -@EnableZuulProxy -// 扫描自定义的servlet(类上标注@WebServle) -@ServletComponentScan @SpringBootApplication public class SopGatewayApplication { diff --git a/sop-gateway/src/main/java/com/gitee/sop/gateway/config/GatewayConfig.java b/sop-gateway/src/main/java/com/gitee/sop/gateway/config/GatewayConfig.java index 3345063d..56d66b9d 100644 --- a/sop-gateway/src/main/java/com/gitee/sop/gateway/config/GatewayConfig.java +++ b/sop-gateway/src/main/java/com/gitee/sop/gateway/config/GatewayConfig.java @@ -1,6 +1,7 @@ package com.gitee.sop.gateway.config; import com.gitee.sop.gatewaycommon.gateway.configuration.AlipayGatewayConfiguration; +import org.springframework.context.annotation.Configuration; /** diff --git a/sop-gateway/src/main/java/com/gitee/sop/gateway/config/ZuulConfig.java b/sop-gateway/src/main/java/com/gitee/sop/gateway/config/ZuulConfig.java index 4f0863a1..a4e04211 100644 --- a/sop-gateway/src/main/java/com/gitee/sop/gateway/config/ZuulConfig.java +++ b/sop-gateway/src/main/java/com/gitee/sop/gateway/config/ZuulConfig.java @@ -13,7 +13,7 @@ import org.springframework.context.annotation.Configuration; * 开通支付宝开放平台能力 * @author tanghc */ -@Configuration +//@Configuration public class ZuulConfig extends AlipayZuulConfiguration { } diff --git a/sop-gateway/src/main/resources/application-dev.properties b/sop-gateway/src/main/resources/application-dev.properties index 21b95d95..8ee3879e 100644 --- a/sop-gateway/src/main/resources/application-dev.properties +++ b/sop-gateway/src/main/resources/application-dev.properties @@ -1,5 +1,5 @@ server.port=8081 -# 固定不变 +# 固定不变,不能改 spring.application.name=sop-gateway # ------- 需要改的配置 ------- @@ -10,56 +10,15 @@ mysql.password=root # nacos地址 nacos.url=127.0.0.1:8848 - -# zipkin服务监控地址,没有开启不用改 -zipkin.url=http://127.0.0.1:9411/ - -# 预发布网关域名 -pre.domain=localhost # ------- 需要改的配置end ------- -# 入口地址,不用改,默认是/zuul -zuul.servlet-path=/api -# 禁用默认的过滤器,不能删,不用改 -zuul.FormBodyWrapperFilter.pre.disable=true -zuul.Servlet30WrapperFilter.pre.disable=true -# 不用改,如果要改,请全局替换修改 -zuul.secret=MZZOUSTua6LzApIWXCwEgbBmxSzpzC -# 不用改 -zuul.rest-default-version=1.0 - -# https://blog.csdn.net/qq_36872046/article/details/81058045 -# 路由转发超时时间,毫秒,默认值1000,详见:RibbonClientConfiguration.DEFAULT_READ_TIMEOUT。 -# 如果微服务端 处理时间过长,会导致ribbon read超时,解决办法将这个值调大一点 -ribbon.ReadTimeout=2000 -# 设置为true(默认false),则所有请求都重试,默认只支持get请求重试 -# 请谨慎设置,因为post请求大多都是写入请求,如果要支持重试,确保服务的幂等性 -ribbon.OkToRetryOnAllOperations=false - # nacos cloud配置 spring.cloud.nacos.discovery.server-addr=${nacos.url} -nacos.config.server-addr=${nacos.url} +# 数据库配置 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://${mysql.host}/sop?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai spring.datasource.username=${mysql.username} spring.datasource.password=${mysql.password} -# 不用改 -mybatis.fill.com.gitee.fastmybatis.core.support.DateFillInsert=gmt_create -mybatis.fill.com.gitee.fastmybatis.core.support.DateFillUpdate=gmt_modified - -# 文件上传配置 -spring.servlet.multipart.enabled=true -# 这里设置大一点没关系,真实大小由upload.max-file-size控制 -spring.servlet.multipart.max-file-size=50MB - -# 允许上传文件大小,不能超过这个值,单位:B,KB,MB -upload.max-file-size=2MB - -# zipkin服务跟踪 -spring.zipkin.base-url=${zipkin.url} -# 设置sleuth收集信息的比率,默认0.1,最大是1,数字越大越耗性能 -spring.sleuth.sampler.probability=1 - logging.level.com.gitee=debug \ No newline at end of file diff --git a/sop-sdk/sdk-java/src/main/java/com/gitee/sop/sdk/client/OpenRequest.java b/sop-sdk/sdk-java/src/main/java/com/gitee/sop/sdk/client/OpenRequest.java index c04f2792..6f55ddf5 100644 --- a/sop-sdk/sdk-java/src/main/java/com/gitee/sop/sdk/client/OpenRequest.java +++ b/sop-sdk/sdk-java/src/main/java/com/gitee/sop/sdk/client/OpenRequest.java @@ -42,7 +42,7 @@ public class OpenRequest { } return openHttp.get(url, header); } else { - return openHttp.requestJson(url, JSON.toJSONString(form), header); + return openHttp.request(url, form, header, requestMethod); } } } catch (IOException e) {