Add support for customizing ConfigurableRequestItemParser for SC gateway and Zuul adapter (#2542)
* Add ConfigurableRequestItemParser and support customize RequestItemParser * fluent-style API
This commit is contained in:
parent
2920efc33b
commit
55ce1a2c11
|
|
@ -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.
|
||||
* <p>
|
||||
* example:
|
||||
* if you want to get client real ip in multi nginx proxy, you can register SentinelGatewayFilter bean as follows
|
||||
*
|
||||
* ConfigurableRequestItemParser<ServerWebExchange> parser = new ConfigurableRequestItemParser<>(new ServerWebExchangeItemParser());
|
||||
* List<String> 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<T> implements RequestItemParser<T> {
|
||||
|
||||
private final List<Function<T, String>> pathExtractors = new ArrayList<>(2);
|
||||
|
||||
private final List<Function<T, String>> remoteAddressExtractors = new ArrayList<>(2);
|
||||
|
||||
private final List<BiFunction<T, String, String>> headerExtractors = new ArrayList<>(2);
|
||||
|
||||
private final List<BiFunction<T, String, String>> urlParamExtractors = new ArrayList<>(2);
|
||||
|
||||
private final List<BiFunction<T, String, String>> cookieValueExtractors = new ArrayList<>(2);
|
||||
|
||||
private final RequestItemParser<T> delegate;
|
||||
|
||||
public ConfigurableRequestItemParser(RequestItemParser<T> delegate) {
|
||||
AssertUtil.notNull(delegate, "delegate can not be null");
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPath(T request) {
|
||||
for (Function<T, String> 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<T, String> 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<T, String, String> 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<T, String, String> 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<T, String, String> extractor : cookieValueExtractors) {
|
||||
String cookie = extractor.apply(request, cookieName);
|
||||
if (StringUtil.isNotBlank(cookie)) {
|
||||
return cookie;
|
||||
}
|
||||
}
|
||||
return delegate.getCookieValue(request, cookieName);
|
||||
}
|
||||
|
||||
public ConfigurableRequestItemParser<T> addPathExtractor(Function<T, String> extractor) {
|
||||
if (extractor == null) {
|
||||
return this;
|
||||
}
|
||||
pathExtractors.add(extractor);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ConfigurableRequestItemParser<T> addRemoteAddressExtractor(Function<T, String> extractor) {
|
||||
if (extractor == null) {
|
||||
return this;
|
||||
}
|
||||
remoteAddressExtractors.add(extractor);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ConfigurableRequestItemParser<T> addHeaderExtractor(BiFunction<T, String, String> extractor) {
|
||||
if (extractor == null) {
|
||||
return this;
|
||||
}
|
||||
headerExtractors.add(extractor);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ConfigurableRequestItemParser<T> addUrlParamExtractor(BiFunction<T, String, String> extractor) {
|
||||
if (extractor == null) {
|
||||
return this;
|
||||
}
|
||||
urlParamExtractors.add(extractor);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ConfigurableRequestItemParser<T> addCookieValueExtractor(BiFunction<T, String, String> extractor) {
|
||||
if (extractor == null) {
|
||||
return this;
|
||||
}
|
||||
cookieValueExtractors.add(extractor);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
@ -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<ServerWebExchange> paramParser;
|
||||
|
||||
public SentinelGatewayFilter() {
|
||||
this(Ordered.HIGHEST_PRECEDENCE);
|
||||
}
|
||||
|
||||
public SentinelGatewayFilter(int order) {
|
||||
this.order = order;
|
||||
this(order, new ServerWebExchangeItemParser());
|
||||
}
|
||||
|
||||
private final GatewayParamParser<ServerWebExchange> paramParser = new GatewayParamParser<>(
|
||||
new ServerWebExchangeItemParser());
|
||||
public SentinelGatewayFilter(RequestItemParser<ServerWebExchange> serverWebExchangeItemParser) {
|
||||
this(Ordered.HIGHEST_PRECEDENCE, serverWebExchangeItemParser);
|
||||
}
|
||||
|
||||
public SentinelGatewayFilter(int order, RequestItemParser<ServerWebExchange> requestItemParser) {
|
||||
AssertUtil.notNull(requestItemParser, "requestItemParser cannot be null");
|
||||
this.order = order;
|
||||
this.paramParser = new GatewayParamParser<>(requestItemParser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
|
|
|
|||
|
|
@ -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<RequestContext> paramParser = new GatewayParamParser<>(
|
||||
new RequestContextItemParser());
|
||||
private final GatewayParamParser<RequestContext> paramParser;
|
||||
|
||||
public SentinelZuulPreFilter() {
|
||||
this(10000);
|
||||
}
|
||||
|
||||
public SentinelZuulPreFilter(int order) {
|
||||
this(order, new RequestContextItemParser());
|
||||
}
|
||||
|
||||
public SentinelZuulPreFilter(int order, RequestItemParser<RequestContext> requestItemParser) {
|
||||
AssertUtil.notNull(requestItemParser, "requestItemParser cannot be null");
|
||||
this.order = order;
|
||||
this.paramParser = new GatewayParamParser<>(requestItemParser);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -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<HttpRequestMessage, String> routeExtractor;
|
||||
|
||||
private final GatewayParamParser<HttpRequestMessage> paramParser = new GatewayParamParser<>(
|
||||
new HttpRequestMessageItemParser());
|
||||
private final GatewayParamParser<HttpRequestMessage> 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<HttpRequestMessage, String> routeExtractor) {
|
||||
this(order, blockedEndpointName, executor, fastError, routeExtractor, new HttpRequestMessageItemParser());
|
||||
}
|
||||
|
||||
public SentinelZuulInboundFilter(int order, String blockedEndpointName, Executor executor, boolean fastError,
|
||||
Function<HttpRequestMessage, String> routeExtractor, RequestItemParser<HttpRequestMessage> 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
|
||||
|
|
|
|||
Loading…
Reference in New Issue