diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/param/ConfigurableRequestItemParser.java b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/param/ConfigurableRequestItemParser.java new file mode 100644 index 00000000..e4b7f80a --- /dev/null +++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/param/ConfigurableRequestItemParser.java @@ -0,0 +1,161 @@ +/* + * Copyright 1999-2022 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.csp.sentinel.adapter.gateway.common.param; + +import com.alibaba.csp.sentinel.util.AssertUtil; +import com.alibaba.csp.sentinel.util.StringUtil; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.BiFunction; +import java.util.function.Function; + +/** + * delegate RequestItemParser, support add extractors to customize request item parse. + *

+ * example: + * if you want to get client real ip in multi nginx proxy, you can register SentinelGatewayFilter bean as follows + * + * ConfigurableRequestItemParser parser = new ConfigurableRequestItemParser<>(new ServerWebExchangeItemParser()); + * List headerNames = Arrays.asList("X-Real-IP", "Client-IP"); + * parser.addRemoteAddressExtractor(serverWebExchange -> { + * for (String headerKey : headerNames) { + * String remoteAddress = serverWebExchange.getRequest().getHeaders().getFirst(headerKey); + * if (StringUtils.hasLength(remoteAddress)) { + * return remoteAddress; + * } + * } + * return null; + * }); + * return new SentinelGatewayFilter(parser); + * + * @author icodening + * @date 2022.01.14 + */ +public class ConfigurableRequestItemParser implements RequestItemParser { + + private final List> pathExtractors = new ArrayList<>(2); + + private final List> remoteAddressExtractors = new ArrayList<>(2); + + private final List> headerExtractors = new ArrayList<>(2); + + private final List> urlParamExtractors = new ArrayList<>(2); + + private final List> cookieValueExtractors = new ArrayList<>(2); + + private final RequestItemParser delegate; + + public ConfigurableRequestItemParser(RequestItemParser delegate) { + AssertUtil.notNull(delegate, "delegate can not be null"); + this.delegate = delegate; + } + + @Override + public String getPath(T request) { + for (Function extractor : pathExtractors) { + String pathValue = extractor.apply(request); + if (StringUtil.isNotBlank(pathValue)) { + return pathValue; + } + } + return delegate.getPath(request); + } + + @Override + public String getRemoteAddress(T request) { + for (Function extractor : remoteAddressExtractors) { + String remoteAddress = extractor.apply(request); + if (StringUtil.isNotBlank(remoteAddress)) { + return remoteAddress; + } + } + return delegate.getRemoteAddress(request); + } + + @Override + public String getHeader(T request, String key) { + for (BiFunction extractor : headerExtractors) { + String headerValue = extractor.apply(request, key); + if (StringUtil.isNotBlank(headerValue)) { + return headerValue; + } + } + return delegate.getHeader(request, key); + } + + @Override + public String getUrlParam(T request, String paramName) { + for (BiFunction extractor : urlParamExtractors) { + String urlParam = extractor.apply(request, paramName); + if (StringUtil.isNotBlank(urlParam)) { + return urlParam; + } + } + return delegate.getUrlParam(request, paramName); + } + + @Override + public String getCookieValue(T request, String cookieName) { + for (BiFunction extractor : cookieValueExtractors) { + String cookie = extractor.apply(request, cookieName); + if (StringUtil.isNotBlank(cookie)) { + return cookie; + } + } + return delegate.getCookieValue(request, cookieName); + } + + public ConfigurableRequestItemParser addPathExtractor(Function extractor) { + if (extractor == null) { + return this; + } + pathExtractors.add(extractor); + return this; + } + + public ConfigurableRequestItemParser addRemoteAddressExtractor(Function extractor) { + if (extractor == null) { + return this; + } + remoteAddressExtractors.add(extractor); + return this; + } + + public ConfigurableRequestItemParser addHeaderExtractor(BiFunction extractor) { + if (extractor == null) { + return this; + } + headerExtractors.add(extractor); + return this; + } + + public ConfigurableRequestItemParser addUrlParamExtractor(BiFunction extractor) { + if (extractor == null) { + return this; + } + urlParamExtractors.add(extractor); + return this; + } + + public ConfigurableRequestItemParser addCookieValueExtractor(BiFunction extractor) { + if (extractor == null) { + return this; + } + cookieValueExtractors.add(extractor); + return this; + } +} diff --git a/sentinel-adapter/sentinel-spring-cloud-gateway-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/sc/SentinelGatewayFilter.java b/sentinel-adapter/sentinel-spring-cloud-gateway-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/sc/SentinelGatewayFilter.java index 3dfbaa5a..a3552b6c 100644 --- a/sentinel-adapter/sentinel-spring-cloud-gateway-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/sc/SentinelGatewayFilter.java +++ b/sentinel-adapter/sentinel-spring-cloud-gateway-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/sc/SentinelGatewayFilter.java @@ -15,21 +15,18 @@ */ package com.alibaba.csp.sentinel.adapter.gateway.sc; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; - import com.alibaba.csp.sentinel.EntryType; import com.alibaba.csp.sentinel.ResourceTypeConstants; import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants; import com.alibaba.csp.sentinel.adapter.gateway.common.param.GatewayParamParser; +import com.alibaba.csp.sentinel.adapter.gateway.common.param.RequestItemParser; +import com.alibaba.csp.sentinel.adapter.gateway.sc.api.GatewayApiMatcherManager; +import com.alibaba.csp.sentinel.adapter.gateway.sc.api.matcher.WebExchangeApiMatcher; import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager; import com.alibaba.csp.sentinel.adapter.reactor.ContextConfig; import com.alibaba.csp.sentinel.adapter.reactor.EntryConfig; import com.alibaba.csp.sentinel.adapter.reactor.SentinelReactorTransformer; -import com.alibaba.csp.sentinel.adapter.gateway.sc.api.GatewayApiMatcherManager; -import com.alibaba.csp.sentinel.adapter.gateway.sc.api.matcher.WebExchangeApiMatcher; - +import com.alibaba.csp.sentinel.util.AssertUtil; import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; @@ -39,6 +36,10 @@ import org.springframework.core.Ordered; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + /** * @author Eric Zhao * @since 1.6.0 @@ -47,16 +48,25 @@ public class SentinelGatewayFilter implements GatewayFilter, GlobalFilter, Order private final int order; + private final GatewayParamParser paramParser; + public SentinelGatewayFilter() { this(Ordered.HIGHEST_PRECEDENCE); } public SentinelGatewayFilter(int order) { - this.order = order; + this(order, new ServerWebExchangeItemParser()); } - private final GatewayParamParser paramParser = new GatewayParamParser<>( - new ServerWebExchangeItemParser()); + public SentinelGatewayFilter(RequestItemParser serverWebExchangeItemParser) { + this(Ordered.HIGHEST_PRECEDENCE, serverWebExchangeItemParser); + } + + public SentinelGatewayFilter(int order, RequestItemParser requestItemParser) { + AssertUtil.notNull(requestItemParser, "requestItemParser cannot be null"); + this.order = order; + this.paramParser = new GatewayParamParser<>(requestItemParser); + } @Override public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulPreFilter.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulPreFilter.java index 649ffa19..95e64328 100644 --- a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulPreFilter.java +++ b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulPreFilter.java @@ -26,6 +26,7 @@ import com.alibaba.csp.sentinel.EntryType; import com.alibaba.csp.sentinel.ResourceTypeConstants; import com.alibaba.csp.sentinel.SphU; import com.alibaba.csp.sentinel.adapter.gateway.common.param.GatewayParamParser; +import com.alibaba.csp.sentinel.adapter.gateway.common.param.RequestItemParser; import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule; import com.alibaba.csp.sentinel.adapter.gateway.zuul.RequestContextItemParser; import com.alibaba.csp.sentinel.adapter.gateway.zuul.api.ZuulGatewayApiMatcherManager; @@ -37,6 +38,7 @@ import com.alibaba.csp.sentinel.adapter.gateway.zuul.fallback.ZuulBlockFallbackM import com.alibaba.csp.sentinel.adapter.gateway.zuul.fallback.ZuulBlockFallbackProvider; import com.alibaba.csp.sentinel.context.ContextUtil; import com.alibaba.csp.sentinel.slots.block.BlockException; +import com.alibaba.csp.sentinel.util.AssertUtil; import com.alibaba.csp.sentinel.util.StringUtil; import com.alibaba.csp.sentinel.util.function.Predicate; @@ -59,15 +61,20 @@ public class SentinelZuulPreFilter extends ZuulFilter { private final int order; - private final GatewayParamParser paramParser = new GatewayParamParser<>( - new RequestContextItemParser()); + private final GatewayParamParser paramParser; public SentinelZuulPreFilter() { this(10000); } public SentinelZuulPreFilter(int order) { + this(order, new RequestContextItemParser()); + } + + public SentinelZuulPreFilter(int order, RequestItemParser requestItemParser) { + AssertUtil.notNull(requestItemParser, "requestItemParser cannot be null"); this.order = order; + this.paramParser = new GatewayParamParser<>(requestItemParser); } @Override diff --git a/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/filters/inbound/SentinelZuulInboundFilter.java b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/filters/inbound/SentinelZuulInboundFilter.java index ff174538..344a2ca3 100644 --- a/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/filters/inbound/SentinelZuulInboundFilter.java +++ b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/filters/inbound/SentinelZuulInboundFilter.java @@ -27,6 +27,7 @@ import com.alibaba.csp.sentinel.EntryType; import com.alibaba.csp.sentinel.ResourceTypeConstants; import com.alibaba.csp.sentinel.SphU; import com.alibaba.csp.sentinel.adapter.gateway.common.param.GatewayParamParser; +import com.alibaba.csp.sentinel.adapter.gateway.common.param.RequestItemParser; import com.alibaba.csp.sentinel.adapter.gateway.zuul2.HttpRequestMessageItemParser; import com.alibaba.csp.sentinel.adapter.gateway.zuul2.api.ZuulGatewayApiMatcherManager; import com.alibaba.csp.sentinel.adapter.gateway.zuul2.api.matcher.HttpRequestMessageApiMatcher; @@ -67,8 +68,7 @@ public class SentinelZuulInboundFilter extends HttpInboundFilter { private final boolean fastError; private final Function routeExtractor; - private final GatewayParamParser paramParser = new GatewayParamParser<>( - new HttpRequestMessageItemParser()); + private final GatewayParamParser paramParser; /** * Constructor of the inbound filter, which extracts the route from the context route VIP attribute by default. @@ -98,13 +98,20 @@ public class SentinelZuulInboundFilter extends HttpInboundFilter { */ public SentinelZuulInboundFilter(int order, String blockedEndpointName, Executor executor, boolean fastError, Function routeExtractor) { + this(order, blockedEndpointName, executor, fastError, routeExtractor, new HttpRequestMessageItemParser()); + } + + public SentinelZuulInboundFilter(int order, String blockedEndpointName, Executor executor, boolean fastError, + Function routeExtractor, RequestItemParser requestItemParser) { AssertUtil.notEmpty(blockedEndpointName, "blockedEndpointName cannot be empty"); AssertUtil.notNull(routeExtractor, "routeExtractor cannot be null"); + AssertUtil.notNull(requestItemParser, "requestItemParser cannot be null"); this.order = order; this.blockedEndpointName = blockedEndpointName; this.executor = executor; this.fastError = fastError; this.routeExtractor = routeExtractor; + this.paramParser = new GatewayParamParser<>(requestItemParser); } @Override