Support parsing cookie as request items in gateway flow control (#814)
- Add `getCookieValue` method in RequestItemParser interface and update GatewayParamParser - Add cookie parsing logic for Spring Cloud Gateway and Zuul Signed-off-by: Eric Zhao <sczyh16@gmail.com>
This commit is contained in:
parent
2eecd3ac6a
commit
08676c4f6e
|
|
@ -30,6 +30,7 @@ public final class SentinelGatewayConstants {
|
||||||
public static final int PARAM_PARSE_STRATEGY_HOST = 1;
|
public static final int PARAM_PARSE_STRATEGY_HOST = 1;
|
||||||
public static final int PARAM_PARSE_STRATEGY_HEADER = 2;
|
public static final int PARAM_PARSE_STRATEGY_HEADER = 2;
|
||||||
public static final int PARAM_PARSE_STRATEGY_URL_PARAM = 3;
|
public static final int PARAM_PARSE_STRATEGY_URL_PARAM = 3;
|
||||||
|
public static final int PARAM_PARSE_STRATEGY_COOKIE = 4;
|
||||||
|
|
||||||
public static final int PARAM_MATCH_STRATEGY_EXACT = 0;
|
public static final int PARAM_MATCH_STRATEGY_EXACT = 0;
|
||||||
public static final int PARAM_MATCH_STRATEGY_PREFIX = 1;
|
public static final int PARAM_MATCH_STRATEGY_PREFIX = 1;
|
||||||
|
|
|
||||||
|
|
@ -42,8 +42,8 @@ public class GatewayParamParser<T> {
|
||||||
/**
|
/**
|
||||||
* Parse parameters for given resource from the request entity on condition of the rule predicate.
|
* Parse parameters for given resource from the request entity on condition of the rule predicate.
|
||||||
*
|
*
|
||||||
* @param resource valid resource name
|
* @param resource valid resource name
|
||||||
* @param request valid request
|
* @param request valid request
|
||||||
* @param rulePredicate rule predicate indicating the rules to refer
|
* @param rulePredicate rule predicate indicating the rules to refer
|
||||||
* @return the parameter array
|
* @return the parameter array
|
||||||
*/
|
*/
|
||||||
|
|
@ -92,6 +92,8 @@ public class GatewayParamParser<T> {
|
||||||
return parseHeader(item, request);
|
return parseHeader(item, request);
|
||||||
case SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM:
|
case SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM:
|
||||||
return parseUrlParameter(item, request);
|
return parseUrlParameter(item, request);
|
||||||
|
case SentinelGatewayConstants.PARAM_PARSE_STRATEGY_COOKIE:
|
||||||
|
return parseCookie(item, request);
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -139,6 +141,17 @@ public class GatewayParamParser<T> {
|
||||||
return parseWithMatchStrategyInternal(item.getMatchStrategy(), param, pattern);
|
return parseWithMatchStrategyInternal(item.getMatchStrategy(), param, pattern);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String parseCookie(/*@Valid*/ GatewayParamFlowItem item, T request) {
|
||||||
|
String cookieName = item.getFieldName();
|
||||||
|
String pattern = item.getPattern();
|
||||||
|
String param = requestItemParser.getCookieValue(request, cookieName);
|
||||||
|
if (pattern == null) {
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
// Match value according to regex pattern or exact mode.
|
||||||
|
return parseWithMatchStrategyInternal(item.getMatchStrategy(), param, pattern);
|
||||||
|
}
|
||||||
|
|
||||||
private String parseWithMatchStrategyInternal(int matchStrategy, String value, String pattern) {
|
private String parseWithMatchStrategyInternal(int matchStrategy, String value, String pattern) {
|
||||||
// TODO: implement here.
|
// TODO: implement here.
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ public interface RequestItemParser<T> {
|
||||||
* Get the header associated with the header key.
|
* Get the header associated with the header key.
|
||||||
*
|
*
|
||||||
* @param request valid request
|
* @param request valid request
|
||||||
* @param key valid header key
|
* @param key valid header key
|
||||||
* @return the header
|
* @return the header
|
||||||
*/
|
*/
|
||||||
String getHeader(T request, String key);
|
String getHeader(T request, String key);
|
||||||
|
|
@ -49,9 +49,19 @@ public interface RequestItemParser<T> {
|
||||||
/**
|
/**
|
||||||
* Get the parameter value associated with the parameter name.
|
* Get the parameter value associated with the parameter name.
|
||||||
*
|
*
|
||||||
* @param request valid request
|
* @param request valid request
|
||||||
* @param paramName valid parameter name
|
* @param paramName valid parameter name
|
||||||
* @return the parameter value
|
* @return the parameter value
|
||||||
*/
|
*/
|
||||||
String getUrlParam(T request, String paramName);
|
String getUrlParam(T request, String paramName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the cookie value associated with the cookie name.
|
||||||
|
*
|
||||||
|
* @param request valid request
|
||||||
|
* @param cookieName valid cookie name
|
||||||
|
* @return the cookie value
|
||||||
|
* @since 1.7.0
|
||||||
|
*/
|
||||||
|
String getCookieValue(T request, String cookieName);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
package com.alibaba.csp.sentinel.adapter.gateway.common.rule;
|
package com.alibaba.csp.sentinel.adapter.gateway.common.rule;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
@ -242,14 +243,18 @@ public final class GatewayRuleManager {
|
||||||
if (item.getParseStrategy() < 0) {
|
if (item.getParseStrategy() < 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (item.getParseStrategy() == SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM ||
|
// Check required field name for item types.
|
||||||
item.getParseStrategy() == SentinelGatewayConstants.PARAM_PARSE_STRATEGY_HEADER) {
|
if (FIELD_REQUIRED_SET.contains(item.getParseStrategy()) && StringUtil.isBlank(item.getFieldName())) {
|
||||||
if (StringUtil.isBlank(item.getFieldName())) {
|
return false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return StringUtil.isEmpty(item.getPattern()) || item.getMatchStrategy() >= 0;
|
return StringUtil.isEmpty(item.getPattern()) || item.getMatchStrategy() >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final Set<Integer> FIELD_REQUIRED_SET = new HashSet<>(
|
||||||
|
Arrays.asList(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM,
|
||||||
|
SentinelGatewayConstants.PARAM_PARSE_STRATEGY_HEADER,
|
||||||
|
SentinelGatewayConstants.PARAM_PARSE_STRATEGY_COOKIE)
|
||||||
|
);
|
||||||
|
|
||||||
private GatewayRuleManager() {}
|
private GatewayRuleManager() {}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -94,6 +94,7 @@ public class GatewayParamParserTest {
|
||||||
final String api1 = "my_test_route_B";
|
final String api1 = "my_test_route_B";
|
||||||
final String headerName = "X-Sentinel-Flag";
|
final String headerName = "X-Sentinel-Flag";
|
||||||
final String paramName = "p";
|
final String paramName = "p";
|
||||||
|
final String cookieName = "myCookie";
|
||||||
GatewayFlowRule routeRuleNoParam = new GatewayFlowRule(routeId1)
|
GatewayFlowRule routeRuleNoParam = new GatewayFlowRule(routeId1)
|
||||||
.setCount(10)
|
.setCount(10)
|
||||||
.setIntervalSec(10);
|
.setIntervalSec(10);
|
||||||
|
|
@ -128,6 +129,13 @@ public class GatewayParamParserTest {
|
||||||
.setParamItem(new GatewayParamFlowItem()
|
.setParamItem(new GatewayParamFlowItem()
|
||||||
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_HOST)
|
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_HOST)
|
||||||
);
|
);
|
||||||
|
GatewayFlowRule routeRule5 = new GatewayFlowRule(routeId1)
|
||||||
|
.setCount(50)
|
||||||
|
.setIntervalSec(30)
|
||||||
|
.setParamItem(new GatewayParamFlowItem()
|
||||||
|
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_COOKIE)
|
||||||
|
.setFieldName(cookieName)
|
||||||
|
);
|
||||||
GatewayFlowRule apiRule1 = new GatewayFlowRule(api1)
|
GatewayFlowRule apiRule1 = new GatewayFlowRule(api1)
|
||||||
.setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME)
|
.setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME)
|
||||||
.setCount(5)
|
.setCount(5)
|
||||||
|
|
@ -140,6 +148,7 @@ public class GatewayParamParserTest {
|
||||||
rules.add(routeRule2);
|
rules.add(routeRule2);
|
||||||
rules.add(routeRule3);
|
rules.add(routeRule3);
|
||||||
rules.add(routeRule4);
|
rules.add(routeRule4);
|
||||||
|
rules.add(routeRule5);
|
||||||
rules.add(routeRuleNoParam);
|
rules.add(routeRuleNoParam);
|
||||||
rules.add(apiRule1);
|
rules.add(apiRule1);
|
||||||
GatewayRuleManager.loadRules(rules);
|
GatewayRuleManager.loadRules(rules);
|
||||||
|
|
@ -148,19 +157,24 @@ public class GatewayParamParserTest {
|
||||||
final String expectedAddress = "66.77.88.99";
|
final String expectedAddress = "66.77.88.99";
|
||||||
final String expectedHeaderValue1 = "Sentinel";
|
final String expectedHeaderValue1 = "Sentinel";
|
||||||
final String expectedUrlParamValue1 = "17";
|
final String expectedUrlParamValue1 = "17";
|
||||||
|
final String expectedCookieValue1 = "Sentinel-Foo";
|
||||||
|
|
||||||
mockClientHostAddress(itemParser, expectedAddress);
|
mockClientHostAddress(itemParser, expectedAddress);
|
||||||
Map<String, String> expectedHeaders = new HashMap<String, String>() {{
|
Map<String, String> expectedHeaders = new HashMap<String, String>() {{
|
||||||
put(headerName, expectedHeaderValue1); put("Host", expectedHost);
|
put(headerName, expectedHeaderValue1); put("Host", expectedHost);
|
||||||
}};
|
}};
|
||||||
mockHeaders(itemParser, expectedHeaders);
|
mockHeaders(itemParser, expectedHeaders);
|
||||||
mockSingleUrlParam(itemParser, paramName, expectedUrlParamValue1);
|
mockSingleUrlParam(itemParser, paramName, expectedUrlParamValue1);
|
||||||
|
mockSingleCookie(itemParser, cookieName, expectedCookieValue1);
|
||||||
|
|
||||||
Object[] params = paramParser.parseParameterFor(routeId1, request, routeIdPredicate);
|
Object[] params = paramParser.parseParameterFor(routeId1, request, routeIdPredicate);
|
||||||
// Param length should be 5 (4 with parameters, 1 normal flow with generated constant)
|
// Param length should be 6 (5 with parameters, 1 normal flow with generated constant)
|
||||||
assertThat(params.length).isEqualTo(5);
|
assertThat(params.length).isEqualTo(6);
|
||||||
assertThat(params[routeRule1.getParamItem().getIndex()]).isEqualTo(expectedAddress);
|
assertThat(params[routeRule1.getParamItem().getIndex()]).isEqualTo(expectedAddress);
|
||||||
assertThat(params[routeRule2.getParamItem().getIndex()]).isEqualTo(expectedHeaderValue1);
|
assertThat(params[routeRule2.getParamItem().getIndex()]).isEqualTo(expectedHeaderValue1);
|
||||||
assertThat(params[routeRule3.getParamItem().getIndex()]).isEqualTo(expectedUrlParamValue1);
|
assertThat(params[routeRule3.getParamItem().getIndex()]).isEqualTo(expectedUrlParamValue1);
|
||||||
assertThat(params[routeRule4.getParamItem().getIndex()]).isEqualTo(expectedHost);
|
assertThat(params[routeRule4.getParamItem().getIndex()]).isEqualTo(expectedHost);
|
||||||
|
assertThat(params[routeRule5.getParamItem().getIndex()]).isEqualTo(expectedCookieValue1);
|
||||||
assertThat(params[params.length - 1]).isEqualTo(SentinelGatewayConstants.GATEWAY_DEFAULT_PARAM);
|
assertThat(params[params.length - 1]).isEqualTo(SentinelGatewayConstants.GATEWAY_DEFAULT_PARAM);
|
||||||
|
|
||||||
assertThat(paramParser.parseParameterFor(api1, request, routeIdPredicate).length).isZero();
|
assertThat(paramParser.parseParameterFor(api1, request, routeIdPredicate).length).isZero();
|
||||||
|
|
@ -196,6 +210,10 @@ public class GatewayParamParserTest {
|
||||||
when(parser.getHeader(any(), eq(key))).thenReturn(value);
|
when(parser.getHeader(any(), eq(key))).thenReturn(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void mockSingleCookie(/*@Mock*/ RequestItemParser parser, String key, String value) {
|
||||||
|
when(parser.getCookieValue(any(), eq(key))).thenReturn(value);
|
||||||
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
GatewayApiDefinitionManager.loadApiDefinitions(new HashSet<ApiDefinition>());
|
GatewayApiDefinitionManager.loadApiDefinitions(new HashSet<ApiDefinition>());
|
||||||
|
|
|
||||||
|
|
@ -16,9 +16,11 @@
|
||||||
package com.alibaba.csp.sentinel.adapter.gateway.sc;
|
package com.alibaba.csp.sentinel.adapter.gateway.sc;
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
import com.alibaba.csp.sentinel.adapter.gateway.common.param.RequestItemParser;
|
import com.alibaba.csp.sentinel.adapter.gateway.common.param.RequestItemParser;
|
||||||
|
|
||||||
|
import org.springframework.http.HttpCookie;
|
||||||
import org.springframework.web.server.ServerWebExchange;
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -50,4 +52,11 @@ public class ServerWebExchangeItemParser implements RequestItemParser<ServerWebE
|
||||||
public String getUrlParam(ServerWebExchange exchange, String paramName) {
|
public String getUrlParam(ServerWebExchange exchange, String paramName) {
|
||||||
return exchange.getRequest().getQueryParams().getFirst(paramName);
|
return exchange.getRequest().getQueryParams().getFirst(paramName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCookieValue(ServerWebExchange exchange, String cookieName) {
|
||||||
|
return Optional.ofNullable(exchange.getResponse().getCookies().getFirst(cookieName))
|
||||||
|
.map(HttpCookie::getValue)
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package com.alibaba.csp.sentinel.adapter.gateway.zuul;
|
package com.alibaba.csp.sentinel.adapter.gateway.zuul;
|
||||||
|
|
||||||
|
import javax.servlet.http.Cookie;
|
||||||
|
|
||||||
import com.alibaba.csp.sentinel.adapter.gateway.common.param.RequestItemParser;
|
import com.alibaba.csp.sentinel.adapter.gateway.common.param.RequestItemParser;
|
||||||
|
|
||||||
import com.netflix.zuul.context.RequestContext;
|
import com.netflix.zuul.context.RequestContext;
|
||||||
|
|
@ -44,4 +46,18 @@ public class RequestContextItemParser implements RequestItemParser<RequestContex
|
||||||
public String getUrlParam(RequestContext requestContext, String paramName) {
|
public String getUrlParam(RequestContext requestContext, String paramName) {
|
||||||
return requestContext.getRequest().getParameter(paramName);
|
return requestContext.getRequest().getParameter(paramName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCookieValue(RequestContext requestContext, String cookieName) {
|
||||||
|
Cookie[] cookies = requestContext.getRequest().getCookies();
|
||||||
|
if (cookies == null || cookieName == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (Cookie cookie : cookies) {
|
||||||
|
if (cookie != null && cookieName.equals(cookie.getName())) {
|
||||||
|
return cookie.getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue