GR0
This commit is contained in:
@@ -32,7 +32,7 @@
|
||||
<commons-codec.version>1.15</commons-codec.version>
|
||||
<commons-pool2.version>2.11.1</commons-pool2.version>
|
||||
<gson.version>2.8.9</gson.version>
|
||||
<netty-tcnative.version>2.0.53.Final</netty-tcnative.version>
|
||||
<netty-tcnative.version>2.0.54.Final</netty-tcnative.version>
|
||||
<spring-cloud.version>2.2.9.RELEASE</spring-cloud.version>
|
||||
<snakeyaml.version>1.30</snakeyaml.version>
|
||||
<spring-data-releasetrain.version>Moore-SR13</spring-data-releasetrain.version>-->
|
||||
|
||||
@@ -17,6 +17,16 @@
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.github.seancfoley</groupId>
|
||||
<artifactId>ipaddress</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>ognl</groupId>
|
||||
<artifactId>ognl</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.openjdk.jol</groupId>
|
||||
<artifactId>jol-core</artifactId>
|
||||
|
||||
@@ -68,7 +68,10 @@ public class FizzServerHttpRequestDecorator extends ServerHttpRequestDecorator {
|
||||
public FizzServerHttpRequestDecorator(ServerHttpRequest delegate) {
|
||||
super(delegate);
|
||||
this.delegate = (AbstractServerHttpRequest) delegate;
|
||||
nativeRequest = this.delegate.getNativeRequest();
|
||||
try {
|
||||
nativeRequest = this.delegate.getNativeRequest();
|
||||
} catch (IllegalStateException e) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -120,6 +123,9 @@ public class FizzServerHttpRequestDecorator extends ServerHttpRequestDecorator {
|
||||
}
|
||||
|
||||
private MultiValueMap<String, HttpCookie> initCookies() {
|
||||
if (nativeRequest == null) {
|
||||
return null;
|
||||
}
|
||||
MultiValueMap<String, HttpCookie> cookies = new LinkedMultiValueMap<>();
|
||||
for (CharSequence name : nativeRequest.cookies().keySet()) {
|
||||
for (Cookie cookie : nativeRequest.cookies().get(name)) {
|
||||
|
||||
47
fizz-common/src/test/java/we/OgnlTests.java
Normal file
47
fizz-common/src/test/java/we/OgnlTests.java
Normal file
@@ -0,0 +1,47 @@
|
||||
package we;
|
||||
|
||||
import ognl.Ognl;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
class OgnlTests {
|
||||
|
||||
@Test
|
||||
void testGet() throws Exception {
|
||||
|
||||
Root root = new Root();
|
||||
|
||||
Map<String, Object> query = new HashMap<>();
|
||||
query.put("version", "v2");
|
||||
query.put("userId", 1234563);
|
||||
query.put("age", 25);
|
||||
|
||||
root.put("query", query);
|
||||
|
||||
Map<String, Object> client = new HashMap<>();
|
||||
client.put("ip", "10.2.3.4");
|
||||
client.put("ip2", "10.2.3.88");
|
||||
root.put("client", client);
|
||||
|
||||
Boolean result = (Boolean) Ognl.getValue("checkIp(client.ip2) && (query.version == 'v2' || query.age < 20) and query.age in (22,25,30) && client.ip=='10.2.3.4'", root);
|
||||
|
||||
System.out.println(result);
|
||||
assertEquals(true, result);
|
||||
}
|
||||
}
|
||||
|
||||
class Root extends HashMap {
|
||||
|
||||
public Root() {
|
||||
}
|
||||
|
||||
public boolean checkIp(String ip) {
|
||||
System.out.println(ip);
|
||||
return ip.equals("10.2.3.88");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -83,6 +83,7 @@ public class AggregateFilter implements WebFilter {
|
||||
String serviceId = WebUtils.getBackendService(exchange);
|
||||
if (serviceId == null) {
|
||||
return chain.filter(exchange);
|
||||
} else if (WebUtils.ignorePlugin(exchange) && WebUtils.getRoute(exchange).type == ApiConfig.Type.SERVICE_AGGREGATE) {
|
||||
} else {
|
||||
byte act = WebUtils.getApiConfigType(exchange);
|
||||
if (act == ApiConfig.Type.UNDEFINED) {
|
||||
|
||||
@@ -44,6 +44,10 @@ public final class FizzPluginFilterChain {
|
||||
}
|
||||
|
||||
public static Mono<Void> next(ServerWebExchange exchange) {
|
||||
if (WebUtils.ignorePlugin(exchange)) {
|
||||
WebFilterChain chain = exchange.getAttribute(WEB_FILTER_CHAIN);
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
Iterator<PluginConfig> it = exchange.getAttribute(pluginConfigsIt);
|
||||
Route route = WebUtils.getRoute(exchange);
|
||||
if (it == null || route.pluginConfigsChange) {
|
||||
|
||||
@@ -47,6 +47,7 @@ public class ApiConfig {
|
||||
static final byte REVERSE_PROXY = 3;
|
||||
static final byte CALLBACK = 4;
|
||||
static final byte DUBBO = 5;
|
||||
static final byte DIRECT_RESPONSE = 6;
|
||||
}
|
||||
|
||||
public static final String ALL_METHOD = "AM";
|
||||
@@ -226,6 +227,7 @@ public class ApiConfig {
|
||||
Route r = new Route().dedicatedLine( this.dedicatedLine)
|
||||
.type( this.type)
|
||||
.method( request.getMethod())
|
||||
.path( this.path)
|
||||
.registryCenter( this.registryCenter)
|
||||
.backendService( this.backendService)
|
||||
.backendPath( this.backendPath)
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
package we.proxy;
|
||||
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import we.plugin.PluginConfig;
|
||||
import we.util.Consts;
|
||||
import we.util.JacksonUtils;
|
||||
@@ -36,6 +37,8 @@ public class Route {
|
||||
|
||||
public HttpMethod method;
|
||||
|
||||
public String path;
|
||||
|
||||
public String registryCenter;
|
||||
|
||||
public String backendService;
|
||||
@@ -68,6 +71,10 @@ public class Route {
|
||||
|
||||
public long retryInterval = 0;
|
||||
|
||||
public MediaType contentType;
|
||||
|
||||
public String body;
|
||||
|
||||
public Route dedicatedLine(boolean b) {
|
||||
dedicatedLine = b;
|
||||
return this;
|
||||
@@ -83,6 +90,11 @@ public class Route {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Route path(String p) {
|
||||
path = p;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Route registryCenter(String rc) {
|
||||
registryCenter = rc;
|
||||
return this;
|
||||
@@ -150,6 +162,16 @@ public class Route {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Route contentType(MediaType type) {
|
||||
contentType = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Route body(String b) {
|
||||
body = b;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public String getBackendPathQuery() {
|
||||
if (query != null) {
|
||||
|
||||
@@ -28,10 +28,7 @@ import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import we.config.AggregateRedisConfig;
|
||||
import we.config.SystemConfig;
|
||||
import we.util.Consts;
|
||||
import we.util.JacksonUtils;
|
||||
import we.util.Result;
|
||||
import we.util.ThreadContext;
|
||||
import we.util.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Collections;
|
||||
@@ -182,7 +179,11 @@ public class RegistryCenterService implements ApplicationListener<ContextRefresh
|
||||
}
|
||||
|
||||
public String getInstance(String registryCenter, String service) {
|
||||
return registryCenterMap.get(registryCenter).getInstance(service);
|
||||
RegistryCenter rc = registryCenterMap.get(registryCenter);
|
||||
if (rc == null) {
|
||||
throw Utils.runtimeExceptionWithoutStack(registryCenter + " not exists");
|
||||
}
|
||||
return rc.getInstance(service);
|
||||
}
|
||||
|
||||
public static String getServiceNameSpace(String registryCenter, String service) {
|
||||
|
||||
@@ -115,10 +115,16 @@ public abstract class WebUtils {
|
||||
|
||||
public static final String ORIGINAL_ERROR = "origerr@";
|
||||
|
||||
public static final String IGNORE_PLUGIN = "ignPlg@";
|
||||
|
||||
|
||||
private WebUtils() {
|
||||
}
|
||||
|
||||
public static boolean ignorePlugin(ServerWebExchange exchange) {
|
||||
return exchange.getAttributes().containsKey(IGNORE_PLUGIN);
|
||||
}
|
||||
|
||||
public static boolean isFavReq(ServerWebExchange exchange) {
|
||||
return exchange.getAttribute(FAV_REQUEST) != null;
|
||||
}
|
||||
|
||||
@@ -17,34 +17,35 @@
|
||||
|
||||
package we.plugin.grayrelease;
|
||||
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
|
||||
import cn.hutool.crypto.symmetric.SymmetricCrypto;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import com.auth0.jwt.JWT;
|
||||
import com.auth0.jwt.interfaces.Claim;
|
||||
import com.auth0.jwt.interfaces.DecodedJWT;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import inet.ipaddr.AddressStringException;
|
||||
import inet.ipaddr.IPAddress;
|
||||
import inet.ipaddr.IPAddressSeqRange;
|
||||
import inet.ipaddr.IPAddressString;
|
||||
import ognl.Ognl;
|
||||
import ognl.OgnlException;
|
||||
import org.apache.logging.log4j.ThreadContext;
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.NettyDataBuffer;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.http.*;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
import we.config.SystemConfig;
|
||||
import we.dedicated_line.DedicatedLineService;
|
||||
import we.plugin.FizzPluginFilterChain;
|
||||
import we.plugin.auth.ApiConfig;
|
||||
import we.plugin.requestbody.RequestBodyPlugin;
|
||||
import we.spring.http.server.reactive.ext.FizzServerHttpRequestDecorator;
|
||||
import we.spring.http.server.reactive.ext.FizzServerHttpResponseDecorator;
|
||||
import we.util.Consts;
|
||||
import we.util.NettyDataBufferUtils;
|
||||
import we.util.WebUtils;
|
||||
import we.proxy.Route;
|
||||
import we.spring.web.server.ext.FizzServerWebExchangeDecorator;
|
||||
import we.util.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Map;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author hongqiaowei
|
||||
@@ -53,91 +54,438 @@ import java.util.Map;
|
||||
@Component(GrayReleasePlugin.GRAY_RELEASE_PLUGIN)
|
||||
public class GrayReleasePlugin extends RequestBodyPlugin {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(GrayReleasePlugin.class);
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(GrayReleasePlugin.class);
|
||||
|
||||
public static final String GRAY_RELEASE_PLUGIN = "GrayReleasePlugin";
|
||||
public static final String GRAY_RELEASE_PLUGIN = "GrayReleasePlugin";
|
||||
|
||||
@Resource
|
||||
private SystemConfig systemConfig;
|
||||
private static final String triggerCondition = "triggerCondition";
|
||||
private static final String routeType = "routeType";
|
||||
private static final String routeConfig = "routeConfig";
|
||||
private static final String routeConfigMap = "routeConfigMap";
|
||||
private static final String method = "method";
|
||||
private static final String path = "path";
|
||||
private static final String contentType = "contentType";
|
||||
private static final String body = "body";
|
||||
private static final String form = "form";
|
||||
private static final String cookie = "cookie";
|
||||
private static final String header = "header";
|
||||
private static final String query = "query";
|
||||
private static final String client = "client";
|
||||
private static final String ip = "ip";
|
||||
|
||||
@Resource
|
||||
private DedicatedLineService dedicatedLineService;
|
||||
private static class OgnlRoot extends HashMap<String, Object> {
|
||||
|
||||
public double random() {
|
||||
return Math.random();
|
||||
}
|
||||
|
||||
public boolean exist(String key) {
|
||||
String[] keys = StringUtils.split(key, Consts.S.COMMA);
|
||||
Map<String, Object> m = this;
|
||||
int keyLen = keys.length;
|
||||
for (int i = 0; i < keyLen; i++) {
|
||||
String k = keys[i];
|
||||
if (m.containsKey(k)) {
|
||||
Object obj = m.get(k);
|
||||
if (obj instanceof Map) {
|
||||
m = (Map<String, Object>) obj;
|
||||
} else if (i + 1 != keyLen) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean matches(String key, String regex) throws OgnlException {
|
||||
String value = (String) Ognl.getValue(key, this);
|
||||
if (value == null) {
|
||||
return false;
|
||||
}
|
||||
return value.matches(regex);
|
||||
}
|
||||
|
||||
public String jwtClaim(String name) {
|
||||
Map<String, Object> headerMap = (Map<String, Object>) get(GrayReleasePlugin.header);
|
||||
if (headerMap == null) {
|
||||
return null;
|
||||
} else {
|
||||
String token = (String) headerMap.get(HttpHeaders.AUTHORIZATION.toLowerCase());
|
||||
if (StringUtils.isBlank(token)) {
|
||||
return null;
|
||||
} else if (token.length() > 7 && token.substring(0, 7).equalsIgnoreCase("Bearer ")) {
|
||||
token = token.substring(7);
|
||||
}
|
||||
DecodedJWT jwt = JWT.decode(token);
|
||||
Claim claim = jwt.getClaim(name);
|
||||
if (claim == null) {
|
||||
return null;
|
||||
}
|
||||
return claim.asString();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean clientIpInRange(String range) throws AddressStringException {
|
||||
Map<String, Object> cli = (Map<String, Object>) get(client);
|
||||
if (cli == null) {
|
||||
return false;
|
||||
} else {
|
||||
String pi = (String) cli.get(ip);
|
||||
if (pi == null) {
|
||||
return false;
|
||||
} else {
|
||||
return ipInRange(pi, range);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean ipInRange(String ip, String range) throws AddressStringException {
|
||||
IPAddress ipAddress = new IPAddressString(ip).toAddress();
|
||||
IPAddress rangeAddress = new IPAddressString(range).getAddress();
|
||||
return rangeAddress.contains(ipAddress);
|
||||
}
|
||||
|
||||
public boolean ipInRange(String ip, String rangeStartIp, String rangeEndIp) throws AddressStringException {
|
||||
IPAddress startIPAddress = new IPAddressString(rangeStartIp).getAddress();
|
||||
IPAddress endIPAddress = new IPAddressString(rangeEndIp).getAddress();
|
||||
IPAddressSeqRange ipRange = startIPAddress.spanWithRange(endIPAddress);
|
||||
IPAddress ipAddress = new IPAddressString(ip).toAddress();
|
||||
return ipRange.contains(ipAddress);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return JacksonUtils.writeValueAsString(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> doFilter(ServerWebExchange exchange, Map<String, Object> config) {
|
||||
String traceId = WebUtils.getTraceId(exchange);
|
||||
ThreadContext.put(Consts.TRACE_ID, traceId);
|
||||
String tc = (String) config.get(triggerCondition);
|
||||
Object ognlRoot = request2ognlContext(exchange);
|
||||
Boolean conditionMatch = false;
|
||||
try {
|
||||
// LogService.setBizId(traceId);
|
||||
String dedicatedLineId = WebUtils.getDedicatedLineId(exchange);
|
||||
String cryptoKey = dedicatedLineService.getRequestCryptoKey(dedicatedLineId);
|
||||
conditionMatch = (Boolean) Ognl.getValue(tc, ognlRoot);
|
||||
} catch (OgnlException e) {
|
||||
LOGGER.error("calc condition expression {} with context {}", tc, ognlRoot, e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
if (conditionMatch) {
|
||||
Route route = WebUtils.getRoute(exchange);
|
||||
changeRoute(exchange, route, config);
|
||||
if (route.type == ApiConfig.Type.DIRECT_RESPONSE) {
|
||||
HttpHeaders hdrs = new HttpHeaders();
|
||||
hdrs.setContentType(route.contentType);
|
||||
return WebUtils.response(exchange, HttpStatus.OK, hdrs, route.body);
|
||||
} else {
|
||||
exchange.getAttributes().put(WebUtils.IGNORE_PLUGIN, Consts.S.EMPTY);
|
||||
}
|
||||
}
|
||||
return FizzPluginFilterChain.next(exchange);
|
||||
}
|
||||
|
||||
FizzServerHttpRequestDecorator request = (FizzServerHttpRequestDecorator) exchange.getRequest();
|
||||
return request.getBody().defaultIfEmpty(NettyDataBufferUtils.EMPTY_DATA_BUFFER).single().flatMap(body -> {
|
||||
if (body != NettyDataBufferUtils.EMPTY_DATA_BUFFER && systemConfig.fizzDedicatedLineClientRequestCrypto()) {
|
||||
byte[] bodyBytes = request.getBodyBytes();
|
||||
request.setBody(decrypt(bodyBytes, cryptoKey));
|
||||
request.getHeaders().remove(HttpHeaders.CONTENT_LENGTH);
|
||||
}
|
||||
private Object request2ognlContext(ServerWebExchange exchange) {
|
||||
OgnlRoot ognlRoot = new OgnlRoot();
|
||||
ServerHttpRequest request = exchange.getRequest();
|
||||
|
||||
ServerHttpResponse original = exchange.getResponse();
|
||||
FizzServerHttpResponseDecorator fizzServerHttpResponseDecorator = new FizzServerHttpResponseDecorator(original) {
|
||||
@Override
|
||||
public Publisher<? extends DataBuffer> writeWith(DataBuffer remoteResponseBody) {
|
||||
if (remoteResponseBody == null || remoteResponseBody == NettyDataBufferUtils.EMPTY_DATA_BUFFER) {
|
||||
return Mono.empty();
|
||||
} else {
|
||||
if (StringUtils.isNotBlank(cryptoKey)) {
|
||||
getDelegate().getHeaders().remove(HttpHeaders.CONTENT_LENGTH);
|
||||
byte[] bytes = remoteResponseBody.asByteBuffer().array();
|
||||
NettyDataBuffer from = NettyDataBufferUtils.from(encrypt(bytes, cryptoKey));
|
||||
return Mono.just(from);
|
||||
} else {
|
||||
return Mono.just(remoteResponseBody);
|
||||
ognlRoot.put(method, request.getMethodValue().toLowerCase());
|
||||
ognlRoot.put(path, WebUtils.getClientReqPath(exchange));
|
||||
|
||||
MultiValueMap<String, String> queryParams = request.getQueryParams();
|
||||
if (!queryParams.isEmpty()) {
|
||||
Map<String, Object> queryMap = new HashMap<>();
|
||||
queryParams.forEach(
|
||||
(name, values) -> {
|
||||
if (CollectionUtils.isEmpty(values)) {
|
||||
queryMap.put(name, null);
|
||||
} else if (values.size() > 1) {
|
||||
queryMap.put(name, values);
|
||||
} else {
|
||||
queryMap.put(name, values.get(0));
|
||||
}
|
||||
}
|
||||
);
|
||||
ognlRoot.put(query, queryMap);
|
||||
}
|
||||
|
||||
HttpHeaders headers = request.getHeaders();
|
||||
if (!headers.isEmpty()) {
|
||||
Map<String, Object> headerMap = new HashMap<>();
|
||||
headers.forEach(
|
||||
(nm, values) -> {
|
||||
String name = nm.toLowerCase();
|
||||
if (CollectionUtils.isEmpty(values)) {
|
||||
headerMap.put(name, null);
|
||||
} else if (values.size() > 1) {
|
||||
headerMap.put(name, values);
|
||||
} else {
|
||||
headerMap.put(name, values.get(0));
|
||||
}
|
||||
}
|
||||
);
|
||||
ognlRoot.put(header, headerMap);
|
||||
}
|
||||
|
||||
MultiValueMap<String, HttpCookie> cookies = request.getCookies();
|
||||
if (!CollectionUtils.isEmpty(cookies)) {
|
||||
Map<String, Object> cookieMap = new HashMap<>();
|
||||
cookies.forEach(
|
||||
(name, values) -> {
|
||||
if (CollectionUtils.isEmpty(values)) {
|
||||
cookieMap.put(name, null);
|
||||
} else if (values.size() > 1) {
|
||||
List<String> lst = new ArrayList<>(values.size());
|
||||
for (HttpCookie value : values) {
|
||||
lst.add(value.getValue());
|
||||
}
|
||||
cookieMap.put(name, lst);
|
||||
} else {
|
||||
cookieMap.put(name, values.get(0).getValue());
|
||||
}
|
||||
}
|
||||
);
|
||||
ognlRoot.put(cookie, cookieMap);
|
||||
}
|
||||
|
||||
MediaType reqContentType = request.getHeaders().getContentType();
|
||||
if (MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith(reqContentType)) {
|
||||
exchange.getFormData()
|
||||
.map(
|
||||
formData -> {
|
||||
if (formData == FizzServerWebExchangeDecorator.EMPTY_FORM_DATA) {
|
||||
return null;
|
||||
} else {
|
||||
Map<String, Object> formMap = new HashMap<>();
|
||||
formData.forEach(
|
||||
(name, values) -> {
|
||||
if (CollectionUtils.isEmpty(values)) {
|
||||
formMap.put(name, null);
|
||||
} else if (values.size() > 1) {
|
||||
formMap.put(name, values);
|
||||
} else {
|
||||
formMap.put(name, values.get(0));
|
||||
}
|
||||
}
|
||||
);
|
||||
ognlRoot.put(form, formMap);
|
||||
return formMap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
ServerWebExchange build = exchange.mutate().response(fizzServerHttpResponseDecorator).build();
|
||||
return FizzPluginFilterChain.next(build);
|
||||
});
|
||||
)
|
||||
.subscribe();
|
||||
} else if (MediaType.APPLICATION_JSON.isCompatibleWith(reqContentType)) {
|
||||
request.getBody()
|
||||
.single()
|
||||
.map(
|
||||
bodyDataBuffer -> {
|
||||
if (bodyDataBuffer == NettyDataBufferUtils.EMPTY_DATA_BUFFER) {
|
||||
return null;
|
||||
} else {
|
||||
String json = bodyDataBuffer.toString(StandardCharsets.UTF_8).trim();
|
||||
if (json.charAt(0) == Consts.S.LEFT_SQUARE_BRACKET) {
|
||||
List<Object> bodyMap = JacksonUtils.readValue(json, new TypeReference<List<Object>>(){});
|
||||
ognlRoot.put(body, bodyMap);
|
||||
} else {
|
||||
Map<String, Object> bodyMap = JacksonUtils.readValue(json, new TypeReference<Map<String, Object>>(){});
|
||||
ognlRoot.put(body, bodyMap);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
)
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
// log.error("{} {} Exception", traceId, DEDICATED_LINE_CODEC_PLUGIN_FILTER, LogService.BIZ_ID, traceId, e);
|
||||
log.error("{} {} Exception", traceId, GRAY_RELEASE_PLUGIN, e);
|
||||
String respJson = WebUtils.jsonRespBody(HttpStatus.INTERNAL_SERVER_ERROR.value(),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase(), traceId);
|
||||
return WebUtils.response(exchange, HttpStatus.INTERNAL_SERVER_ERROR, null, respJson);
|
||||
String originIp = WebUtils.getOriginIp(exchange);
|
||||
if (originIp != null) {
|
||||
Map<String, Object> clientMap = new HashMap<>();
|
||||
clientMap.put(ip, originIp);
|
||||
ognlRoot.put(client, clientMap);
|
||||
}
|
||||
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("request {} ognl root: {}", request.getId(), ognlRoot);
|
||||
}
|
||||
|
||||
return ognlRoot;
|
||||
}
|
||||
|
||||
private void changeRoute(ServerWebExchange exchange, Route route, Map<String, Object> pluginConfig) {
|
||||
byte rt = ((Integer) pluginConfig.get(routeType)).byteValue();
|
||||
route.type = rt;
|
||||
Map<String, String> newRouteConfig = (Map<String, String>) pluginConfig.get(routeConfigMap);
|
||||
if (newRouteConfig == null) {
|
||||
newRouteConfig = routeConfig2map((String) pluginConfig.get(routeConfig));
|
||||
pluginConfig.put(routeConfigMap, newRouteConfig);
|
||||
pluginConfig.remove(routeConfig);
|
||||
}
|
||||
if (rt == ApiConfig.Type.SERVICE_DISCOVERY) {
|
||||
changeServiceDiscoveryRoute(exchange, route, newRouteConfig);
|
||||
} else if (rt == ApiConfig.Type.REVERSE_PROXY) {
|
||||
changeReverseProxyRoute(exchange, pluginConfig, route, newRouteConfig);
|
||||
} else if (rt == ApiConfig.Type.SERVICE_AGGREGATE) {
|
||||
changeAggregateRoute(exchange, route, newRouteConfig);
|
||||
} else {
|
||||
String ct = (String) pluginConfig.get(contentType);
|
||||
String b = (String) pluginConfig.get(body);
|
||||
route.contentType(MediaType.valueOf(ct))
|
||||
.body(b);
|
||||
}
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("route is changed: {}", route);
|
||||
}
|
||||
}
|
||||
|
||||
/*public String encrypt(String data, String secretKey) {
|
||||
if (StringUtils.isBlank(data)) {
|
||||
return data;
|
||||
private void changeServiceDiscoveryRoute(ServerWebExchange exchange, Route route, Map<String, String> newRouteConfig) {
|
||||
String type = newRouteConfig.get("type");
|
||||
String service = newRouteConfig.get("serviceName");
|
||||
if (StringUtils.isNotBlank(service)) {
|
||||
route.backendService = service;
|
||||
}
|
||||
String timeout = newRouteConfig.get("timeout");
|
||||
if (StringUtils.isNotBlank(timeout)) {
|
||||
route.timeout(Long.parseLong(timeout));
|
||||
}
|
||||
if (type.equals("http")) {
|
||||
String registry = newRouteConfig.get("registry");
|
||||
if (StringUtils.isNotBlank(registry)) {
|
||||
route.registryCenter = registry;
|
||||
}
|
||||
String method = newRouteConfig.get("methodName");
|
||||
if (StringUtils.isNotBlank(method)) {
|
||||
route.method(HttpMethod.resolve(method));
|
||||
}
|
||||
String path = newRouteConfig.get("path");
|
||||
if (StringUtils.isNotBlank(path)) {
|
||||
route.backendPath = UrlTransformUtils.transform(route.path, path, WebUtils.getClientReqPath(exchange));
|
||||
}
|
||||
String qry = newRouteConfig.get("query");
|
||||
if (StringUtils.isNotBlank(qry)) {
|
||||
route.query = qry;
|
||||
}
|
||||
String retryCount = newRouteConfig.get("retryCount");
|
||||
if (StringUtils.isNotBlank(retryCount)) {
|
||||
route.retryCount(Integer.parseInt(retryCount));
|
||||
}
|
||||
String retryInterval = newRouteConfig.get("retryInterval");
|
||||
if (StringUtils.isNotBlank(retryInterval)) {
|
||||
route.retryInterval(Long.parseLong(retryInterval));
|
||||
}
|
||||
} else {
|
||||
route.type = ApiConfig.Type.DUBBO;
|
||||
String method = newRouteConfig.get("methodName");
|
||||
if (StringUtils.isNotBlank(method)) {
|
||||
route.rpcMethod(method);
|
||||
}
|
||||
String version = newRouteConfig.get("version");
|
||||
if (StringUtils.isNotBlank(version)) {
|
||||
route.rpcVersion(version);
|
||||
}
|
||||
String group = newRouteConfig.get("group");
|
||||
if (StringUtils.isNotBlank(group)) {
|
||||
route.rpcGroup(group);
|
||||
}
|
||||
String paramTypes = newRouteConfig.get("paramTypes");
|
||||
if (StringUtils.isNotBlank(paramTypes)) {
|
||||
route.rpcParamTypes(paramTypes);
|
||||
}
|
||||
}
|
||||
byte[] key = SecureUtil.decode(secretKey);
|
||||
SymmetricCrypto symmetric = new SymmetricCrypto(SymmetricAlgorithm.AES, key);
|
||||
return symmetric.encryptBase64(data);
|
||||
}*/
|
||||
|
||||
public byte[] encrypt(byte[] data, String secretKey) {
|
||||
byte[] key = SecureUtil.decode(secretKey);
|
||||
SymmetricCrypto symmetric = new SymmetricCrypto(SymmetricAlgorithm.AES, key);
|
||||
return symmetric.encrypt(data);
|
||||
}
|
||||
|
||||
/*public String decrypt(String data, String secretKey) {
|
||||
if (StringUtils.isBlank(data)) {
|
||||
return data;
|
||||
private void changeReverseProxyRoute(ServerWebExchange exchange, Map<String, Object> pluginConfig, Route route, Map<String, String> newRouteConfig) {
|
||||
List<String> httpHostPorts = (List<String>) pluginConfig.get("httpHostPorts");
|
||||
if (httpHostPorts == null) {
|
||||
String httpHostPortStr = newRouteConfig.get("serviceName");
|
||||
if (StringUtils.isBlank(httpHostPortStr)) {
|
||||
httpHostPorts = WebUtils.getApiConfig(exchange).httpHostPorts;
|
||||
} else {
|
||||
httpHostPorts = Arrays.asList(StringUtils.split(httpHostPortStr, Consts.S.COMMA));
|
||||
}
|
||||
pluginConfig.put("httpHostPorts", httpHostPorts);
|
||||
newRouteConfig.remove("serviceName");
|
||||
}
|
||||
byte[] key = SecureUtil.decode(secretKey);
|
||||
SymmetricCrypto symmetric = new SymmetricCrypto(SymmetricAlgorithm.AES, key);
|
||||
return symmetric.decryptStr(data);
|
||||
}*/
|
||||
int counter = (int) pluginConfig.getOrDefault("counter", 0);
|
||||
counter++;
|
||||
if (counter < 0) {
|
||||
counter = Math.abs(counter);
|
||||
}
|
||||
String hostPort = httpHostPorts.get(
|
||||
counter % httpHostPorts.size()
|
||||
);
|
||||
route.nextHttpHostPort(hostPort);
|
||||
pluginConfig.put("counter", counter);
|
||||
|
||||
public byte[] decrypt(byte[] data, String secretKey) {
|
||||
byte[] key = SecureUtil.decode(secretKey);
|
||||
SymmetricCrypto symmetric = new SymmetricCrypto(SymmetricAlgorithm.AES, key);
|
||||
return symmetric.decrypt(data);
|
||||
String method = newRouteConfig.get("methodName");
|
||||
if (StringUtils.isNotBlank(method)) {
|
||||
route.method(HttpMethod.resolve(method));
|
||||
}
|
||||
|
||||
String path = newRouteConfig.get("path");
|
||||
if (StringUtils.isNotBlank(path)) {
|
||||
route.backendPath = UrlTransformUtils.transform(route.path, path, WebUtils.getClientReqPath(exchange));
|
||||
}
|
||||
|
||||
String qry = newRouteConfig.get("query");
|
||||
if (StringUtils.isNotBlank(qry)) {
|
||||
route.query = qry;
|
||||
}
|
||||
|
||||
String timeout = newRouteConfig.get("timeout");
|
||||
if (StringUtils.isNotBlank(timeout)) {
|
||||
route.timeout(Long.parseLong(timeout));
|
||||
}
|
||||
|
||||
String retryCount = newRouteConfig.get("retryCount");
|
||||
if (StringUtils.isNotBlank(retryCount)) {
|
||||
route.retryCount(Integer.parseInt(retryCount));
|
||||
}
|
||||
|
||||
String retryInterval = newRouteConfig.get("retryInterval");
|
||||
if (StringUtils.isNotBlank(retryInterval)) {
|
||||
route.retryInterval(Long.parseLong(retryInterval));
|
||||
}
|
||||
}
|
||||
|
||||
private void changeAggregateRoute(ServerWebExchange exchange, Route route, Map<String, String> newRouteConfig) {
|
||||
String service = newRouteConfig.get("serviceName");
|
||||
if (StringUtils.isNotBlank(service)) {
|
||||
route.backendService = service;
|
||||
WebUtils.setBackendService(exchange, route.backendService);
|
||||
}
|
||||
String path = newRouteConfig.get("path");
|
||||
if (StringUtils.isNotBlank(path)) {
|
||||
route.backendPath = UrlTransformUtils.transform(route.path, path, WebUtils.getClientReqPath(exchange));
|
||||
WebUtils.setBackendPath(exchange, route.backendPath);
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, String> routeConfig2map(String config) {
|
||||
Map<String, String> result = new HashMap<>();
|
||||
String[] lines = StringUtils.split(config, Consts.S.LF);
|
||||
for (String line : lines) {
|
||||
/*int colonIdx = line.indexOf(Consts.S.COLON);
|
||||
int start = 0, end = 0;
|
||||
for (int i = 0; i < line.length(); i++) {
|
||||
if (line.charAt(i) != Consts.S.SPACE) {
|
||||
start = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (int i = line.length() - 1; i > -1; i--) {
|
||||
if (line.charAt(i) != Consts.S.SPACE) {
|
||||
end = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
String name = line.substring(start, colonIdx);
|
||||
String value = line.substring(colonIdx + 1, end + 1);
|
||||
result.put(name, value);*/
|
||||
String[] nameValue = StringUtils.split(line, Consts.S.COLON);
|
||||
result.put(nameValue[0].trim(), nameValue[1].trim());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
package we.plugin.grayrelease;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
import reactor.core.publisher.Mono;
|
||||
import we.plugin.FizzPluginFilterChain;
|
||||
import we.proxy.Route;
|
||||
import we.util.Consts;
|
||||
import we.util.WebUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class GrayReleasePluginTests {
|
||||
|
||||
@Test
|
||||
public void simpleTest() {
|
||||
WebTestClient client = WebTestClient.bindToWebHandler(
|
||||
exchange -> {
|
||||
ServerHttpResponse r = exchange.getResponse();
|
||||
r.setStatusCode(HttpStatus.OK);
|
||||
r.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN_VALUE);
|
||||
return r.writeWith(Mono.just(r.bufferFactory().wrap("this is web handler response".getBytes())));
|
||||
}
|
||||
)
|
||||
.webFilter(
|
||||
(exchange, chain) -> {
|
||||
GrayReleasePlugin grayReleasePlugin = new GrayReleasePlugin();
|
||||
Map<String, Object> config = new HashMap<>();
|
||||
config.put("triggerCondition", "method == 'get'");
|
||||
config.put("routeType", 2);
|
||||
config.put("routeConfig", "type: http \n serviceName: bservice");
|
||||
|
||||
// exchange.getAttributes().put("pcsit@", Collections.emptyIterator());
|
||||
exchange.getAttributes().put(WebUtils.ROUTE, new Route());
|
||||
exchange.getAttributes().put(WebUtils.IGNORE_PLUGIN, Consts.S.EMPTY);
|
||||
exchange.getAttributes().put(FizzPluginFilterChain.WEB_FILTER_CHAIN, chain);
|
||||
exchange.getAttributes().put("oi@", "11.238.145.181");
|
||||
|
||||
return grayReleasePlugin.filter(exchange, config);
|
||||
}
|
||||
)
|
||||
.build();
|
||||
|
||||
client.get()
|
||||
.uri("/proxy/aservice/apath")
|
||||
//.header("h1", "v1")
|
||||
.exchange()
|
||||
.expectBody(String.class).value(
|
||||
v -> {
|
||||
System.err.println("body:\n" + v);
|
||||
}
|
||||
)
|
||||
;
|
||||
}
|
||||
}
|
||||
18
fizz-plugin/src/test/resources/log4j2-test.xml
Normal file
18
fizz-plugin/src/test/resources/log4j2-test.xml
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<Configuration status="info">
|
||||
<properties>
|
||||
<property name="APP_NAME">fizz-plugin</property>
|
||||
</properties>
|
||||
<Appenders>
|
||||
<Console name="Console" target="SYSTEM_OUT">
|
||||
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %level %logger{36} - %msg%n"/>
|
||||
</Console>
|
||||
</Appenders>
|
||||
<Loggers>
|
||||
<Root level="warn">
|
||||
<AppenderRef ref="Console"/>
|
||||
</Root>
|
||||
<Logger name="we" level="debug"/>
|
||||
</Loggers>
|
||||
</Configuration>
|
||||
14
pom.xml
14
pom.xml
@@ -22,7 +22,7 @@
|
||||
<r2dbc-mysql.version>0.8.2</r2dbc-mysql.version>
|
||||
<reflections.version>0.9.11</reflections.version>
|
||||
<commons-pool2.version>2.11.1</commons-pool2.version>
|
||||
<netty-tcnative.version>2.0.53.Final</netty-tcnative.version>
|
||||
<netty-tcnative.version>2.0.54.Final</netty-tcnative.version>
|
||||
<spring-cloud.version>2.2.9.RELEASE</spring-cloud.version>
|
||||
<snakeyaml.version>1.30</snakeyaml.version>
|
||||
<spring-data-releasetrain.version>Moore-SR13</spring-data-releasetrain.version>
|
||||
@@ -70,6 +70,18 @@
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.github.seancfoley</groupId>
|
||||
<artifactId>ipaddress</artifactId>
|
||||
<version>5.3.4</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>ognl</groupId>
|
||||
<artifactId>ognl</artifactId>
|
||||
<version>3.3.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.openjdk.jol</groupId>
|
||||
<artifactId>jol-core</artifactId>
|
||||
|
||||
Reference in New Issue
Block a user