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:
icodening 2022-01-20 10:37:23 +08:00 committed by GitHub
parent 2920efc33b
commit 55ce1a2c11
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 199 additions and 14 deletions

View File

@ -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;
}
}

View File

@ -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) {

View File

@ -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

View File

@ -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