diff --git a/sop-admin/sop-admin-server/pom.xml b/sop-admin/sop-admin-server/pom.xml index f3e41d94..6a032d17 100644 --- a/sop-admin/sop-admin-server/pom.xml +++ b/sop-admin/sop-admin-server/pom.xml @@ -30,7 +30,7 @@ com.gitee.sop sop-registry-api - 1.12.0-SNAPSHOT + 1.12.1-SNAPSHOT diff --git a/sop-common/pom.xml b/sop-common/pom.xml index 627e7d0d..6b19dffd 100644 --- a/sop-common/pom.xml +++ b/sop-common/pom.xml @@ -5,7 +5,7 @@ 4.0.0 com.gitee.sop sop-common - 1.12.0-SNAPSHOT + 1.12.1-SNAPSHOT pom diff --git a/sop-common/sop-gateway-common/pom.xml b/sop-common/sop-gateway-common/pom.xml index 8532ebda..223f94fa 100644 --- a/sop-common/sop-gateway-common/pom.xml +++ b/sop-common/sop-gateway-common/pom.xml @@ -5,11 +5,11 @@ com.gitee.sop sop-common - 1.12.0-SNAPSHOT + 1.12.1-SNAPSHOT ../pom.xml sop-gateway-common - 1.12.0-SNAPSHOT + 1.12.1-SNAPSHOT jar sop-gateway-common diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/SopConstants.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/SopConstants.java index c0ba6457..7ed69b01 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/SopConstants.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/SopConstants.java @@ -9,7 +9,7 @@ import java.nio.charset.StandardCharsets; public class SopConstants { private SopConstants() {} - + public static final Charset CHARSET_UTF8 = StandardCharsets.UTF_8; public static final String UTF8 = "UTF-8"; public static final String FORMAT_JSON = "json"; @@ -51,5 +51,7 @@ public class SopConstants { public static final String SOP_MSG_CHANNEL_PATH = "/com.gitee.sop.channel"; - + public static final String UNKNOWN_SERVICE= "_sop_unknown_service_"; + public static final String UNKNOWN_METHOD = "_sop_unknown_method_"; + public static final String UNKNOWN_VERSION = "_sop_unknown_version_"; } diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/GatewayContext.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/GatewayContext.java index c1fe98ec..8b7e2d2c 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/GatewayContext.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/GatewayContext.java @@ -1,9 +1,78 @@ package com.gitee.sop.gatewaycommon.gateway; import com.gitee.sop.gatewaycommon.bean.ApiContext; +import com.gitee.sop.gatewaycommon.bean.SopConstants; +import com.gitee.sop.gatewaycommon.param.ApiParam; +import com.gitee.sop.gatewaycommon.util.RequestUtil; +import org.springframework.http.HttpMethod; +import org.springframework.util.MultiValueMap; +import org.springframework.web.server.ServerWebExchange; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +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 */ public class GatewayContext extends ApiContext { + + /** + * 获取请求参数 + * @param exchange + * @return + */ + public static ApiParam getApiParam(ServerWebExchange exchange) { + return exchange.getAttribute(SopConstants.CACHE_API_PARAM); + } + + /** + * 设置请求参数 + * @param exchange + * @param apiParam + */ + public static void setApiParam(ServerWebExchange exchange, ApiParam apiParam) { + exchange.getAttributes().put(SopConstants.CACHE_API_PARAM, apiParam); + } + + /** + * 获取Spring Cloud Gateway请求的原始参数。前提是要使用ReadBodyRoutePredicateFactory + * @param exchange + * @return 没有参数返回null + * @see com.gitee.sop.gatewaycommon.gateway.route.ReadBodyRoutePredicateFactory + */ + public static Map getRequestParams(ServerWebExchange exchange) { + Map params = exchange.getAttribute(CACHE_REQUEST_BODY_FOR_MAP); + if (params != null) { + return params; + } + if (exchange.getRequest().getMethod() == HttpMethod.GET) { + MultiValueMap queryParams = exchange.getRequest().getQueryParams(); + params = buildParams(queryParams); + } else { + String cachedBody = exchange.getAttribute(CACHE_REQUEST_BODY_OBJECT_KEY); + if (cachedBody != null) { + params = RequestUtil.parseQueryToMap(cachedBody); + } + } + if (params != null) { + exchange.getAttributes().put(CACHE_REQUEST_BODY_FOR_MAP, params); + } + return params; + } + + private 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()) { + String val = entry.getValue().get(0); + params.put(entry.getKey(), val); + } + return params; + } } diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/LimitFilter.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/LimitFilter.java index 4cdee962..084560bb 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/LimitFilter.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/LimitFilter.java @@ -3,8 +3,8 @@ package com.gitee.sop.gatewaycommon.gateway.filter; import com.gitee.sop.gatewaycommon.bean.ApiConfig; import com.gitee.sop.gatewaycommon.bean.ConfigLimitDto; import com.gitee.sop.gatewaycommon.bean.RouteConfig; -import com.gitee.sop.gatewaycommon.bean.SopConstants; import com.gitee.sop.gatewaycommon.exception.ApiException; +import com.gitee.sop.gatewaycommon.gateway.GatewayContext; import com.gitee.sop.gatewaycommon.limit.LimitManager; import com.gitee.sop.gatewaycommon.limit.LimitType; import com.gitee.sop.gatewaycommon.manager.LimitConfigManager; @@ -35,7 +35,7 @@ public class LimitFilter implements GlobalFilter, Ordered { if (!apiConfig.isOpenLimit()) { return chain.filter(exchange); } - ApiParam apiParam = exchange.getAttribute(SopConstants.CACHE_API_PARAM); + ApiParam apiParam = GatewayContext.getApiParam(exchange); ConfigLimitDto configLimitDto = this.findConfigLimitDto(apiConfig, apiParam, exchange); if (configLimitDto == null) { return null; 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 index 32dc3baa..36911028 100644 --- 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 @@ -2,8 +2,8 @@ package com.gitee.sop.gatewaycommon.gateway.filter; import com.gitee.sop.gatewaycommon.bean.ApiConfig; import com.gitee.sop.gatewaycommon.bean.ApiContext; -import com.gitee.sop.gatewaycommon.bean.SopConstants; import com.gitee.sop.gatewaycommon.exception.ApiException; +import com.gitee.sop.gatewaycommon.gateway.GatewayContext; import com.gitee.sop.gatewaycommon.param.ApiParam; import com.gitee.sop.gatewaycommon.validate.Validator; import lombok.extern.slf4j.Slf4j; @@ -24,7 +24,7 @@ public class ValidateFilter implements GlobalFilter, Ordered { ApiConfig apiConfig = ApiContext.getApiConfig(); // 解析参数 ApiParam param = apiConfig.getGatewayParamBuilder().build(exchange); - exchange.getAttributes().put(SopConstants.CACHE_API_PARAM, param); + GatewayContext.setApiParam(exchange, param); // 验证操作,这里有负责验证签名参数 Validator validator = apiConfig.getValidator(); try { 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 index 19b17100..e0f1ae96 100644 --- 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 @@ -1,9 +1,10 @@ package com.gitee.sop.gatewaycommon.gateway.param; -import com.gitee.sop.gatewaycommon.bean.SopConstants; +import com.gitee.sop.gatewaycommon.gateway.GatewayContext; import com.gitee.sop.gatewaycommon.param.BaseParamBuilder; import org.springframework.web.server.ServerWebExchange; +import java.util.Collections; import java.util.Map; /** @@ -12,7 +13,8 @@ import java.util.Map; public class GatewayParamBuilder extends BaseParamBuilder { @Override - public Map buildRequestParams(ServerWebExchange request) { - return request.getAttribute(SopConstants.CACHE_REQUEST_BODY_FOR_MAP); + public Map buildRequestParams(ServerWebExchange exchange) { + Map params = GatewayContext.getRequestParams(exchange); + return params == null ? Collections.emptyMap() : params; } } 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 f9f14d68..f3beb109 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 @@ -3,22 +3,12 @@ package com.gitee.sop.gatewaycommon.gateway.route; import com.gitee.sop.gatewaycommon.bean.TargetRoute; import com.gitee.sop.gatewaycommon.manager.RouteRepository; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.BeanFactory; -import org.springframework.beans.factory.BeanFactoryAware; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.gateway.event.PredicateArgsEvent; import org.springframework.cloud.gateway.event.RefreshRoutesEvent; -import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition; -import org.springframework.cloud.gateway.handler.predicate.RoutePredicateFactory; import org.springframework.cloud.gateway.route.RouteDefinition; import org.springframework.cloud.gateway.route.RouteDefinitionRepository; -import org.springframework.cloud.gateway.support.ConfigurationUtils; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisherAware; -import org.springframework.core.convert.ConversionService; -import org.springframework.expression.spel.standard.SpelExpressionParser; -import org.springframework.validation.Validator; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -39,27 +29,17 @@ import static java.util.Collections.synchronizedMap; @Slf4j public class GatewayRouteRepository implements ApplicationEventPublisherAware, RouteDefinitionRepository, - BeanFactoryAware, RouteRepository { private final Map routes = synchronizedMap(new LinkedHashMap<>()); - private final SpelExpressionParser parser = new SpelExpressionParser(); - - @Autowired - private ConversionService conversionService; - - @Autowired - private Validator validator; - private ApplicationEventPublisher publisher; - private BeanFactory beanFactory; - @Override public Flux getRouteDefinitions() { List list = routes.values().parallelStream() .map(TargetRoute::getTargetRouteDefinition) + .filter(routeDefinition -> !routeDefinition.getId().contains("_first.route_")) .collect(Collectors.toList()); return Flux.fromIterable(list); } @@ -97,7 +77,6 @@ public class GatewayRouteRepository implements ApplicationEventPublisherAware, public String add(GatewayTargetRoute targetRoute) { GatewayRouteDefinition baseRouteDefinition = targetRoute.getRouteDefinition(); routes.put(baseRouteDefinition.getId(), targetRoute); - this.initPredicateDefinition(targetRoute); this.publisher.publishEvent(new RefreshRoutesEvent(this)); return "success"; } @@ -108,23 +87,6 @@ public class GatewayRouteRepository implements ApplicationEventPublisherAware, routes.put(baseRouteDefinition.getId(), targetRoute); } - protected void initPredicateDefinition(GatewayTargetRoute targetRoute) { - GatewayRouteDefinition routeDefinition = targetRoute.getRouteDefinition(); - RouteDefinition targetRouteDefinition = targetRoute.getTargetRouteDefinition(); - for (PredicateDefinition predicate : targetRouteDefinition.getPredicates()) { - Map args = predicate.getArgs(); - if (!args.isEmpty()) { - RoutePredicateFactory factory = new NameVersionRoutePredicateFactory(); - Map properties = factory.shortcutType().normalize(args, factory, this.parser, this.beanFactory); - Object config = factory.newConfig(); - ConfigurationUtils.bind(config, properties, factory.shortcutFieldPrefix(), predicate.getName(), - validator, conversionService); - this.publisher.publishEvent(new PredicateArgsEvent(this, routeDefinition.getId(), properties)); - } - } - - } - /** * 删除路由 */ @@ -150,8 +112,4 @@ public class GatewayRouteRepository implements ApplicationEventPublisherAware, this.publisher = applicationEventPublisher; } - @Override - public void setBeanFactory(BeanFactory beanFactory) throws BeansException { - this.beanFactory = beanFactory; - } } \ No newline at end of file diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/GatewayZookeeperRouteManager.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/GatewayZookeeperRouteManager.java index e515df03..b77844e0 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/GatewayZookeeperRouteManager.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/GatewayZookeeperRouteManager.java @@ -12,6 +12,7 @@ import org.springframework.core.env.Environment; import java.net.URI; import java.util.ArrayList; +import java.util.LinkedList; import java.util.List; /** @@ -44,7 +45,7 @@ public class GatewayZookeeperRouteManager extends BaseRouteManager filterDefinitionList = new ArrayList<>(gatewayRouteDefinition.getFilters().size()); - List predicateDefinitionList = new ArrayList<>(gatewayRouteDefinition.getPredicates().size()); + LinkedList predicateDefinitionList = new LinkedList<>(); for (GatewayFilterDefinition filter : gatewayRouteDefinition.getFilters()) { FilterDefinition filterDefinition = new FilterDefinition(); BeanUtils.copyProperties(filter, filterDefinition); @@ -56,10 +57,28 @@ public class GatewayZookeeperRouteManager extends BaseRouteManager predicateDefinitionList, String name, String args) { + for (PredicateDefinition predicateDefinition : predicateDefinitionList) { + // 如果已经存在,直接返回 + if (predicateDefinition.getName().equals(name)) { + return; + } + } + predicateDefinitionList.addFirst(new PredicateDefinition(name + "=" + args)); + } + } 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 index b13a53c6..10ffb40e 100644 --- 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 @@ -1,10 +1,11 @@ package com.gitee.sop.gatewaycommon.gateway.route; import com.gitee.sop.gatewaycommon.bean.SopConstants; +import com.gitee.sop.gatewaycommon.gateway.GatewayContext; import com.gitee.sop.gatewaycommon.param.ParamNames; -import com.gitee.sop.gatewaycommon.util.RequestUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory; +import org.springframework.util.CollectionUtils; import org.springframework.validation.annotation.Validated; import org.springframework.web.server.ServerWebExchange; @@ -15,17 +16,15 @@ import java.util.Map; import java.util.function.Predicate; /** + * 此断言决定执行哪个路由 + * * @author tanghc */ @Slf4j public class NameVersionRoutePredicateFactory extends AbstractRoutePredicateFactory { - public static final String PARAM_KEY = "param"; - public static final String REGEXP_KEY = "regexp"; - - private static final String CACHE_REQUEST_BODY_OBJECT_KEY = SopConstants.CACHE_REQUEST_BODY_OBJECT_KEY; - public static final String CACHE_REQUEST_BODY_FOR_MAP = SopConstants.CACHE_REQUEST_BODY_FOR_MAP; - + private static final String PARAM_KEY = "param"; + private static final String REGEXP_KEY = "regexp"; public NameVersionRoutePredicateFactory() { super(Config.class); @@ -37,7 +36,7 @@ public class NameVersionRoutePredicateFactory extends AbstractRoutePredicateFact } /** - * config.param为nameVersion + * config.param为nameVersion,即路由id * * @param config * @return 返回断言 @@ -46,21 +45,14 @@ public class NameVersionRoutePredicateFactory extends AbstractRoutePredicateFact public Predicate apply(Config config) { return exchange -> { - String cachedBody = exchange.getAttribute(CACHE_REQUEST_BODY_OBJECT_KEY); - if (cachedBody == null) { + Map params = GatewayContext.getRequestParams(exchange); + if (CollectionUtils.isEmpty(params)) { return false; } - Map params = exchange.getAttribute(CACHE_REQUEST_BODY_FOR_MAP); - if (params == null) { - params = RequestUtil.parseQueryToMap(cachedBody); - exchange.getAttributes().put(CACHE_REQUEST_BODY_FOR_MAP, params); - } - String nameVersion = config.param; - String name = params.getOrDefault(ParamNames.API_NAME, String.valueOf(System.currentTimeMillis())); + String name = params.getOrDefault(ParamNames.API_NAME, SopConstants.UNKNOWN_METHOD); String version = params.getOrDefault(ParamNames.VERSION_NAME, ""); - boolean match = (name + version).equals(nameVersion); - return match; + return (name + version).equals(nameVersion); }; } 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 index dc5b3259..8dfd7f05 100644 --- 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 @@ -1,19 +1,145 @@ 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; /** - * 获取form表单插件,使用方式: + * 获取请求参数插件,兼容get,post,使用方式: * @Bean * ReadBodyRoutePredicateFactory readBodyRoutePredicateFactory() { * return new ReadBodyRoutePredicateFactory(); * } * + * @see org.springframework.cloud.gateway.handler.predicate.ReadBodyPredicateFactory + * 详见:https://blog.51cto.com/thinklili/2329184 * @author tanghc */ -public class ReadBodyRoutePredicateFactory extends ReadBodyPredicateFactory { +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> messageReaders = 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 match 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(), messageReaders) + .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() { @@ -22,4 +148,51 @@ public class ReadBodyRoutePredicateFactory extends ReadBodyPredicateFactory { 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/DefaultRouteConfigManager.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/DefaultRouteConfigManager.java index 5b56bb66..800a675f 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/DefaultRouteConfigManager.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/DefaultRouteConfigManager.java @@ -38,9 +38,8 @@ public class DefaultRouteConfigManager implements RouteConfigManager { routeConfig = newRouteConfig(); routeConfig.setRouteId(routeId); routeConfigMap.put(routeId, routeConfig); - } else { - MyBeanUtil.copyPropertiesIgnoreNull(res, routeConfig); } + MyBeanUtil.copyPropertiesIgnoreNull(res, routeConfig); routeConfig.initRateLimiter(); } diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/param/ParamNames.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/param/ParamNames.java index 5d5e0928..5167423e 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/param/ParamNames.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/param/ParamNames.java @@ -42,5 +42,8 @@ public class ParamNames { /** */ public static String TIMESTAMP_PATTERN = "yyyy-MM-dd HH:mm:ss"; + /** 返回sign名称 */ + public static String RESPONSE_SIGN_NAME = "sign"; + } 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 34ca08cc..c2340712 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 @@ -145,15 +145,15 @@ public abstract class BaseExecutorAdapter implements ResultExecutor protected ApiInfo getApiInfo(T request) { Map params = this.getApiParam(request); - String name = this.getParamValue(params, ParamNames.API_NAME, "method.unknown"); - String version = this.getParamValue(params, ParamNames.VERSION_NAME, "version.unknown"); + 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); String serviceId = Optional.ofNullable(targetRoute) .flatMap(route -> Optional.ofNullable(route.getServiceRouteInfo())) .map(BaseServiceRouteInfo::getServiceId) - .orElse("serviceId.unknown"); + .orElse(SopConstants.UNKNOWN_SERVICE); BaseRouteDefinition baseRouteDefinition = Optional.ofNullable(targetRoute) .map(route -> route.getRouteDefinition()) @@ -182,7 +182,7 @@ public abstract class BaseExecutorAdapter implements ResultExecutor } public String merge(T exchange, JSONObject responseData) { - JSONObject finalData = new JSONObject(); + JSONObject finalData = new JSONObject(true); Map params = this.getApiParam(exchange); String name = this.getParamValue(params, ParamNames.API_NAME, ERROR_METHOD); ApiConfig apiConfig = ApiConfig.getInstance(); @@ -200,9 +200,10 @@ public abstract class BaseExecutorAdapter implements ResultExecutor if (apiConfig.isShowReturnSign() && !CollectionUtils.isEmpty(params)) { // 添加try...catch,生成sign出错不影响结果正常返回 try { - String sign = this.createResponseSign(apiConfig, params, responseData.toJSONString()); + String responseSignContent = this.buildResponseSignContent(responseDataNodeName, finalData); + String sign = this.createResponseSign(apiConfig, params, responseSignContent); if (StringUtils.hasLength(sign)) { - finalData.put(ParamNames.SIGN_NAME, sign); + finalData.put(ParamNames.RESPONSE_SIGN_NAME, sign); } } catch (Exception e) { log.error("生成平台签名失败, params: {}, serviceResult:{}", JSON.toJSONString(params), responseData, e); @@ -211,6 +212,24 @@ public abstract class BaseExecutorAdapter implements ResultExecutor return finalData.toJSONString(); } + /** + * 获取待签名内容 + * + * @param rootNodeName 业务数据节点 + * @param finalData 最终结果 + * @return 返回待签名内容 + */ + protected String buildResponseSignContent(String rootNodeName, JSONObject finalData) { + String body = finalData.toJSONString(); + int indexOfRootNode = body.indexOf(rootNodeName); + if (indexOfRootNode > 0) { + int signDataStartIndex = indexOfRootNode + rootNodeName.length() + 2; + int length = body.length() - 1; + return body.substring(signDataStartIndex, length); + } + return null; + } + protected String getParamValue(Map apiParam, String key, String defaultValue) { return CollectionUtils.isEmpty(apiParam) ? defaultValue : (String) apiParam.getOrDefault(key, defaultValue); } @@ -219,12 +238,15 @@ public abstract class BaseExecutorAdapter implements ResultExecutor /** * 这里需要使用平台的私钥生成一个sign,需要配置两套公私钥。 * - * @param apiConfig 配置 - * @param params 请求参数 - * @param serviceResult 业务返回结果 + * @param apiConfig 配置 + * @param params 请求参数 + * @param responseSignContent 待签名内容 * @return 返回平台生成的签名 */ - protected String createResponseSign(ApiConfig apiConfig, Map params, String serviceResult) { + protected String createResponseSign(ApiConfig apiConfig, Map params, String responseSignContent) { + if (StringUtils.isEmpty(responseSignContent)) { + return null; + } IsvManager isvManager = apiConfig.getIsvManager(); // 根据appId获取秘钥 String appKey = this.getParamValue(params, ParamNames.APP_KEY_NAME, ""); @@ -240,7 +262,7 @@ public abstract class BaseExecutorAdapter implements ResultExecutor .map(String::valueOf) .orElse(SopConstants.UTF8); String signType = getParamValue(params, ParamNames.SIGN_TYPE_NAME, AlipayConstants.SIGN_TYPE_RSA2); - return AlipaySignature.rsaSign(serviceResult, privateKeyPlatform, charset, signType); + return AlipaySignature.rsaSign(responseSignContent, privateKeyPlatform, charset, signType); } @Getter diff --git a/sop-common/sop-registry-api/pom.xml b/sop-common/sop-registry-api/pom.xml index 83bd1dd1..e613f1b4 100644 --- a/sop-common/sop-registry-api/pom.xml +++ b/sop-common/sop-registry-api/pom.xml @@ -5,7 +5,7 @@ 4.0.0 com.gitee.sop sop-registry-api - 1.12.0-SNAPSHOT + 1.12.1-SNAPSHOT UTF-8 diff --git a/sop-common/sop-service-common/pom.xml b/sop-common/sop-service-common/pom.xml index cda7acb7..ac643b22 100644 --- a/sop-common/sop-service-common/pom.xml +++ b/sop-common/sop-service-common/pom.xml @@ -6,11 +6,11 @@ com.gitee.sop sop-common - 1.12.0-SNAPSHOT + 1.12.1-SNAPSHOT ../pom.xml sop-service-common - 1.12.0-SNAPSHOT + 1.12.1-SNAPSHOT jar sop-service-common diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/manager/ServiceZookeeperApiMetaManager.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/manager/ServiceZookeeperApiMetaManager.java index 7dc95210..2807e123 100644 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/manager/ServiceZookeeperApiMetaManager.java +++ b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/manager/ServiceZookeeperApiMetaManager.java @@ -4,7 +4,6 @@ import com.alibaba.fastjson.JSON; import com.gitee.sop.servercommon.bean.ServiceApiInfo; import com.gitee.sop.servercommon.bean.ServiceConstants; import com.gitee.sop.servercommon.bean.ZookeeperTool; -import com.gitee.sop.servercommon.route.GatewayPredicateDefinition; import com.gitee.sop.servercommon.route.GatewayRouteDefinition; import com.gitee.sop.servercommon.route.ServiceRouteInfo; import lombok.Getter; @@ -16,7 +15,6 @@ import org.springframework.core.env.Environment; import org.springframework.util.StringUtils; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -36,14 +34,6 @@ public class ServiceZookeeperApiMetaManager implements ApiMetaManager { public static final String SOP_SERVICE_ROUTE_PATH = ServiceConstants.SOP_SERVICE_ROUTE_PATH; public static final String PATH_START_CHAR = "/"; - /** - * NameVersion=alipay.story.get1.0 - * see com.gitee.sop.gatewaycommon.routeDefinition.NameVersionRoutePredicateFactory - */ - private static String QUERY_PREDICATE_DEFINITION_TPL = "NameVersion=%s"; - - private static ServiceApiInfo.ApiMeta FIRST_API_META = new ServiceApiInfo.ApiMeta("_first.route_", "/", "v_000"); - private final String routeRootPath = SOP_SERVICE_ROUTE_PATH; private Environment environment; @@ -99,8 +89,7 @@ public class ServiceZookeeperApiMetaManager implements ApiMetaManager { */ protected ServiceRouteInfo buildServiceGatewayInfo(ServiceApiInfo serviceApiInfo) { List apis = serviceApiInfo.getApis(); - List routeDefinitionList = new ArrayList<>(apis.size() + 1); - routeDefinitionList.add(this.buildReadBodyRouteDefinition(serviceApiInfo)); + List routeDefinitionList = new ArrayList<>(apis.size()); for (ServiceApiInfo.ApiMeta apiMeta : apis) { GatewayRouteDefinition gatewayRouteDefinition = this.buildGatewayRouteDefinition(serviceApiInfo, apiMeta); routeDefinitionList.add(gatewayRouteDefinition); @@ -118,32 +107,12 @@ public class ServiceZookeeperApiMetaManager implements ApiMetaManager { return serviceRouteInfo; } - /** - * 添加com.gitee.sop.gatewaycommon.routeDefinition.ReadBodyRoutePredicateFactory,解决form表单获取不到问题 - * - * @return 返回路由定义 - */ - protected GatewayRouteDefinition buildReadBodyRouteDefinition(ServiceApiInfo serviceApiInfo) { - GatewayRouteDefinition readBodyRouteDefinition = this.buildGatewayRouteDefinition(serviceApiInfo, FIRST_API_META); - readBodyRouteDefinition.setOrder(Integer.MIN_VALUE); - - GatewayPredicateDefinition gatewayPredicateDefinition = new GatewayPredicateDefinition(); - gatewayPredicateDefinition.setName("ReadBody"); - GatewayPredicateDefinition readerBodyPredicateDefinition = this.buildNameVersionPredicateDefinition(FIRST_API_META); - List predicates = Arrays.asList(gatewayPredicateDefinition, readerBodyPredicateDefinition); - readBodyRouteDefinition.setPredicates(predicates); - - return readBodyRouteDefinition; - } - protected GatewayRouteDefinition buildGatewayRouteDefinition(ServiceApiInfo serviceApiInfo, ServiceApiInfo.ApiMeta apiMeta) { GatewayRouteDefinition gatewayRouteDefinition = new GatewayRouteDefinition(); // 唯一id规则:接口名 + 版本号 BeanUtils.copyProperties(apiMeta, gatewayRouteDefinition); gatewayRouteDefinition.setId(apiMeta.fetchNameVersion()); gatewayRouteDefinition.setFilters(Collections.emptyList()); - List predicates = Arrays.asList(this.buildNameVersionPredicateDefinition(apiMeta)); - gatewayRouteDefinition.setPredicates(predicates); String uri = this.buildUri(serviceApiInfo, apiMeta); String path = this.buildServletPath(serviceApiInfo, apiMeta); gatewayRouteDefinition.setUri(uri); @@ -166,10 +135,6 @@ public class ServiceZookeeperApiMetaManager implements ApiMetaManager { return servletPath; } - protected GatewayPredicateDefinition buildNameVersionPredicateDefinition(ServiceApiInfo.ApiMeta apiMeta) { - return new GatewayPredicateDefinition(String.format(QUERY_PREDICATE_DEFINITION_TPL, apiMeta.fetchNameVersion())); - } - /** * 上传接口信息到zookeeper * diff --git a/sop-example/sop-auth/pom.xml b/sop-example/sop-auth/pom.xml index c51520ed..614784d8 100644 --- a/sop-example/sop-auth/pom.xml +++ b/sop-example/sop-auth/pom.xml @@ -26,7 +26,7 @@ com.gitee.sop sop-service-common - 1.12.0-SNAPSHOT + 1.12.1-SNAPSHOT org.springframework.cloud diff --git a/sop-example/sop-book/sop-book-web/pom.xml b/sop-example/sop-book/sop-book-web/pom.xml index e2fa4087..3354d840 100644 --- a/sop-example/sop-book/sop-book-web/pom.xml +++ b/sop-example/sop-book/sop-book-web/pom.xml @@ -24,7 +24,7 @@ com.gitee.sop sop-service-common - 1.12.0-SNAPSHOT + 1.12.1-SNAPSHOT com.gitee.sop diff --git a/sop-example/sop-easyopen/pom.xml b/sop-example/sop-easyopen/pom.xml index f4bd1d23..3dc21475 100644 --- a/sop-example/sop-easyopen/pom.xml +++ b/sop-example/sop-easyopen/pom.xml @@ -29,7 +29,7 @@ com.gitee.sop sop-service-common - 1.12.0-SNAPSHOT + 1.12.1-SNAPSHOT org.springframework.cloud diff --git a/sop-example/sop-springmvc/pom.xml b/sop-example/sop-springmvc/pom.xml index 0aea2861..e3a1d8d4 100644 --- a/sop-example/sop-springmvc/pom.xml +++ b/sop-example/sop-springmvc/pom.xml @@ -20,7 +20,7 @@ com.gitee.sop sop-service-common - 1.12.0-SNAPSHOT + 1.12.1-SNAPSHOT diff --git a/sop-example/sop-story/sop-story-web/pom.xml b/sop-example/sop-story/sop-story-web/pom.xml index c9118361..889978af 100644 --- a/sop-example/sop-story/sop-story-web/pom.xml +++ b/sop-example/sop-story/sop-story-web/pom.xml @@ -24,7 +24,7 @@ com.gitee.sop sop-service-common - 1.12.0-SNAPSHOT + 1.12.1-SNAPSHOT com.gitee.sop diff --git a/sop-gateway/pom.xml b/sop-gateway/pom.xml index 534f30d5..10badb21 100644 --- a/sop-gateway/pom.xml +++ b/sop-gateway/pom.xml @@ -23,7 +23,7 @@ com.gitee.sop sop-gateway-common - 1.12.0-SNAPSHOT + 1.12.1-SNAPSHOT diff --git a/sop-gateway/src/main/java/com/gitee/sop/gateway/controller/RedirectController.java b/sop-gateway/src/main/java/com/gitee/sop/gateway/controller/RedirectController.java index 3127d28f..a1d09f27 100644 --- a/sop-gateway/src/main/java/com/gitee/sop/gateway/controller/RedirectController.java +++ b/sop-gateway/src/main/java/com/gitee/sop/gateway/controller/RedirectController.java @@ -6,7 +6,10 @@ import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; +import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; /** * @author tanghc @@ -18,14 +21,15 @@ public class RedirectController { private String path; @RequestMapping("/{method}/{version}/") - public String redirect( + public void redirect( @PathVariable("method") String method , @PathVariable("version") String version , HttpServletRequest request - ) { + , HttpServletResponse response + ) throws ServletException, IOException { request.setAttribute(SopConstants.REDIRECT_METHOD_KEY, method); request.setAttribute(SopConstants.REDIRECT_VERSION_KEY, version); - return "forward:" + path; + request.getRequestDispatcher(path).forward(request, response); } } diff --git a/sop-website/website-server/pom.xml b/sop-website/website-server/pom.xml index 2e35282c..3fe34ed0 100644 --- a/sop-website/website-server/pom.xml +++ b/sop-website/website-server/pom.xml @@ -25,7 +25,7 @@ com.gitee.sop sop-registry-api - 1.12.0-SNAPSHOT + 1.12.1-SNAPSHOT