- 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>master
@@ -30,6 +30,7 @@ public final class SentinelGatewayConstants { | |||
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_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_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. | |||
* | |||
* @param resource valid resource name | |||
* @param request valid request | |||
* @param resource valid resource name | |||
* @param request valid request | |||
* @param rulePredicate rule predicate indicating the rules to refer | |||
* @return the parameter array | |||
*/ | |||
@@ -92,6 +92,8 @@ public class GatewayParamParser<T> { | |||
return parseHeader(item, request); | |||
case SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM: | |||
return parseUrlParameter(item, request); | |||
case SentinelGatewayConstants.PARAM_PARSE_STRATEGY_COOKIE: | |||
return parseCookie(item, request); | |||
default: | |||
return null; | |||
} | |||
@@ -139,6 +141,17 @@ public class GatewayParamParser<T> { | |||
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) { | |||
// TODO: implement here. | |||
if (value == null) { | |||
@@ -41,7 +41,7 @@ public interface RequestItemParser<T> { | |||
* Get the header associated with the header key. | |||
* | |||
* @param request valid request | |||
* @param key valid header key | |||
* @param key valid header key | |||
* @return the header | |||
*/ | |||
String getHeader(T request, String key); | |||
@@ -49,9 +49,19 @@ public interface RequestItemParser<T> { | |||
/** | |||
* Get the parameter value associated with the parameter name. | |||
* | |||
* @param request valid request | |||
* @param request valid request | |||
* @param paramName valid parameter name | |||
* @return the parameter value | |||
*/ | |||
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; | |||
import java.util.ArrayList; | |||
import java.util.Arrays; | |||
import java.util.HashMap; | |||
import java.util.HashSet; | |||
import java.util.List; | |||
@@ -242,14 +243,18 @@ public final class GatewayRuleManager { | |||
if (item.getParseStrategy() < 0) { | |||
return false; | |||
} | |||
if (item.getParseStrategy() == SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM || | |||
item.getParseStrategy() == SentinelGatewayConstants.PARAM_PARSE_STRATEGY_HEADER) { | |||
if (StringUtil.isBlank(item.getFieldName())) { | |||
return false; | |||
} | |||
// Check required field name for item types. | |||
if (FIELD_REQUIRED_SET.contains(item.getParseStrategy()) && StringUtil.isBlank(item.getFieldName())) { | |||
return false; | |||
} | |||
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() {} | |||
} |
@@ -94,6 +94,7 @@ public class GatewayParamParserTest { | |||
final String api1 = "my_test_route_B"; | |||
final String headerName = "X-Sentinel-Flag"; | |||
final String paramName = "p"; | |||
final String cookieName = "myCookie"; | |||
GatewayFlowRule routeRuleNoParam = new GatewayFlowRule(routeId1) | |||
.setCount(10) | |||
.setIntervalSec(10); | |||
@@ -128,6 +129,13 @@ public class GatewayParamParserTest { | |||
.setParamItem(new GatewayParamFlowItem() | |||
.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) | |||
.setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME) | |||
.setCount(5) | |||
@@ -140,6 +148,7 @@ public class GatewayParamParserTest { | |||
rules.add(routeRule2); | |||
rules.add(routeRule3); | |||
rules.add(routeRule4); | |||
rules.add(routeRule5); | |||
rules.add(routeRuleNoParam); | |||
rules.add(apiRule1); | |||
GatewayRuleManager.loadRules(rules); | |||
@@ -148,19 +157,24 @@ public class GatewayParamParserTest { | |||
final String expectedAddress = "66.77.88.99"; | |||
final String expectedHeaderValue1 = "Sentinel"; | |||
final String expectedUrlParamValue1 = "17"; | |||
final String expectedCookieValue1 = "Sentinel-Foo"; | |||
mockClientHostAddress(itemParser, expectedAddress); | |||
Map<String, String> expectedHeaders = new HashMap<String, String>() {{ | |||
put(headerName, expectedHeaderValue1); put("Host", expectedHost); | |||
}}; | |||
mockHeaders(itemParser, expectedHeaders); | |||
mockSingleUrlParam(itemParser, paramName, expectedUrlParamValue1); | |||
mockSingleCookie(itemParser, cookieName, expectedCookieValue1); | |||
Object[] params = paramParser.parseParameterFor(routeId1, request, routeIdPredicate); | |||
// Param length should be 5 (4 with parameters, 1 normal flow with generated constant) | |||
assertThat(params.length).isEqualTo(5); | |||
// Param length should be 6 (5 with parameters, 1 normal flow with generated constant) | |||
assertThat(params.length).isEqualTo(6); | |||
assertThat(params[routeRule1.getParamItem().getIndex()]).isEqualTo(expectedAddress); | |||
assertThat(params[routeRule2.getParamItem().getIndex()]).isEqualTo(expectedHeaderValue1); | |||
assertThat(params[routeRule3.getParamItem().getIndex()]).isEqualTo(expectedUrlParamValue1); | |||
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(paramParser.parseParameterFor(api1, request, routeIdPredicate).length).isZero(); | |||
@@ -196,6 +210,10 @@ public class GatewayParamParserTest { | |||
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 | |||
public void setUp() { | |||
GatewayApiDefinitionManager.loadApiDefinitions(new HashSet<ApiDefinition>()); | |||
@@ -16,9 +16,11 @@ | |||
package com.alibaba.csp.sentinel.adapter.gateway.sc; | |||
import java.net.InetSocketAddress; | |||
import java.util.Optional; | |||
import com.alibaba.csp.sentinel.adapter.gateway.common.param.RequestItemParser; | |||
import org.springframework.http.HttpCookie; | |||
import org.springframework.web.server.ServerWebExchange; | |||
/** | |||
@@ -50,4 +52,11 @@ public class ServerWebExchangeItemParser implements RequestItemParser<ServerWebE | |||
public String getUrlParam(ServerWebExchange exchange, String 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; | |||
import javax.servlet.http.Cookie; | |||
import com.alibaba.csp.sentinel.adapter.gateway.common.param.RequestItemParser; | |||
import com.netflix.zuul.context.RequestContext; | |||
@@ -44,4 +46,18 @@ public class RequestContextItemParser implements RequestItemParser<RequestContex | |||
public String getUrlParam(RequestContext requestContext, String 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; | |||
} | |||
} |