Merge pull request #474 from fizzgate/develop
This commit is contained in:
@@ -4,7 +4,7 @@ English | [简体中文](./README.md)
|
||||
<a href="https://www.fizzgate.com"><img src="https://raw.githubusercontent.com/wiki/fizzgate/fizz-gateway-community/img/icon-color.png" width="70%"></a>
|
||||
</p>
|
||||
<p>
|
||||
<img alt="Version" src="https://img.shields.io/badge/version-2.7.1-blue.svg?cacheSeconds=2592000" />
|
||||
<img alt="Version" src="https://img.shields.io/badge/version-2.7.2-blue.svg?cacheSeconds=2592000" />
|
||||
<a href="http://www.fizzgate.com/fizz-gateway-community/" target="_blank">
|
||||
<img alt="Documentation" src="https://img.shields.io/badge/documentation-yes-brightgreen.svg" />
|
||||
</a>
|
||||
@@ -107,6 +107,7 @@ Starting from v1.3.0, the frontend and backend of the management backend are mer
|
||||
| v2.6.6 | v2.6.6 |
|
||||
| v2.7.0 | v2.7.0 |
|
||||
| v2.7.1 | v2.7.1 |
|
||||
| v2.7.2 | v2.7.2 |
|
||||
|
||||
|
||||
Please download the corresponding management backend version according to the version of the community version
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<a href="https://www.fizzgate.com"><img src="https://raw.githubusercontent.com/wiki/fizzgate/fizz-gateway-community/img/icon-color.png" width="70%"></a>
|
||||
</p>
|
||||
<p>
|
||||
<img alt="Version" src="https://img.shields.io/badge/version-2.7.1-blue.svg?cacheSeconds=2592000" />
|
||||
<img alt="Version" src="https://img.shields.io/badge/version-2.7.2-blue.svg?cacheSeconds=2592000" />
|
||||
<a href="http://www.fizzgate.com/fizz-gateway-community/" target="_blank">
|
||||
<img alt="Documentation" src="https://img.shields.io/badge/documentation-yes-brightgreen.svg" />
|
||||
</a>
|
||||
@@ -109,6 +109,7 @@ API地址:http://demo.fizzgate.com/proxy/[服务名]/[API_Path]
|
||||
| v2.6.6 | v2.6.6 |
|
||||
| v2.7.0 | v2.7.0 |
|
||||
| v2.7.1 | v2.7.1 |
|
||||
| v2.7.2 | v2.7.2 |
|
||||
|
||||
|
||||
请根据社区版的版本下载对应的管理后台版本
|
||||
|
||||
@@ -2,7 +2,7 @@ version: "3.6"
|
||||
|
||||
services:
|
||||
fizz-mysql:
|
||||
image: "fizzgate/fizz-mysql:2.7.1"
|
||||
image: "fizzgate/fizz-mysql:2.7.2"
|
||||
container_name: fizz-mysql
|
||||
restart: always
|
||||
hostname: fizz-mysql
|
||||
@@ -27,7 +27,7 @@ services:
|
||||
- fizz
|
||||
|
||||
fizz-gateway-community:
|
||||
image: "fizzgate/fizz-gateway-community:2.7.1"
|
||||
image: "fizzgate/fizz-gateway-community:2.7.2"
|
||||
container_name: fizz-gateway-community
|
||||
restart: always
|
||||
hostname: fizz-gateway-community
|
||||
@@ -48,7 +48,7 @@ services:
|
||||
- fizz
|
||||
|
||||
fizz-manager-professional:
|
||||
image: "fizzgate/fizz-manager-professional:2.7.1"
|
||||
image: "fizzgate/fizz-manager-professional:2.7.2"
|
||||
container_name: fizz-manager-professional
|
||||
restart: always
|
||||
hostname: fizz-manager-professional
|
||||
|
||||
@@ -166,11 +166,11 @@ var common = {
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取步骤中调用的接口的响应头
|
||||
* 获取步骤中调用的接口的响应体
|
||||
* @param {*} ctx 上下文 【必填】
|
||||
* @param {*} stepName 步骤名【必填】
|
||||
* @param {*} requestName 请求的接口名 【必填】
|
||||
* @param {*} field 字段名 【选填】,不传时返回整个响应头
|
||||
* @param {*} field 字段名 【选填】,不传时返回整个响应体
|
||||
*/
|
||||
getStepRespBody: function (ctx, stepName, requestName, field){
|
||||
var resp = this.getStepResp(ctx, stepName, requestName);
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<artifactId>fizz-gateway-community</artifactId>
|
||||
<groupId>com.fizzgate</groupId>
|
||||
<version>2.7.1</version>
|
||||
<version>2.7.2</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -14,11 +14,11 @@
|
||||
|
||||
<properties>
|
||||
<!--<java.version>1.8</java.version>
|
||||
<spring-framework.version>5.2.22.RELEASE</spring-framework.version>
|
||||
<spring-framework.version>5.2.23.RELEASE</spring-framework.version>
|
||||
<spring-session-bom.version>Dragonfruit-SR3</spring-session-bom.version>
|
||||
<reactor-bom.version>Dysprosium-SR25</reactor-bom.version>
|
||||
<lettuce.version>5.3.7.RELEASE</lettuce.version>
|
||||
<netty.version>4.1.89.Final</netty.version>
|
||||
<netty.version>4.1.91.Final</netty.version>
|
||||
<httpcore.version>4.4.16</httpcore.version>
|
||||
<log4j2.version>2.17.2</log4j2.version>
|
||||
<slf4j.version>1.7.36</slf4j.version>
|
||||
|
||||
@@ -166,11 +166,11 @@ var common = {
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取步骤中调用的接口的响应头
|
||||
* 获取步骤中调用的接口的响应体
|
||||
* @param {*} ctx 上下文 【必填】
|
||||
* @param {*} stepName 步骤名【必填】
|
||||
* @param {*} requestName 请求的接口名 【必填】
|
||||
* @param {*} field 字段名 【选填】,不传时返回整个响应头
|
||||
* @param {*} field 字段名 【选填】,不传时返回整个响应体
|
||||
*/
|
||||
getStepRespBody: function (ctx, stepName, requestName, field){
|
||||
var resp = this.getStepResp(ctx, stepName, requestName);
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>fizz-gateway-community</artifactId>
|
||||
<groupId>com.fizzgate</groupId>
|
||||
<version>2.7.1</version>
|
||||
<version>2.7.2</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>fizz-gateway-community</artifactId>
|
||||
<groupId>com.fizzgate</groupId>
|
||||
<version>2.7.1</version>
|
||||
<version>2.7.2</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -56,13 +56,19 @@ public class FilterExceptionHandlerConfig {
|
||||
|
||||
public static class FilterExceptionHandler implements WebExceptionHandler {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(FilterExceptionHandler.class);
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(FilterExceptionHandler.class);
|
||||
private static final String filterExceptionHandler = "filterExceptionHandler";
|
||||
|
||||
@Override
|
||||
public Mono<Void> handle(ServerWebExchange exchange, Throwable t) {
|
||||
exchange.getAttributes().put(WebUtils.ORIGINAL_ERROR, t);
|
||||
String traceId = WebUtils.getTraceId(exchange);
|
||||
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
org.apache.logging.log4j.ThreadContext.put(Consts.TRACE_ID, traceId);
|
||||
LOGGER.debug(Consts.S.EMPTY, t);
|
||||
}
|
||||
|
||||
ServerHttpResponse resp = exchange.getResponse();
|
||||
if (SystemConfig.FIZZ_ERR_RESP_HTTP_STATUS_ENABLE) {
|
||||
if (t instanceof ResponseStatusException) {
|
||||
@@ -113,9 +119,8 @@ public class FilterExceptionHandlerConfig {
|
||||
|
||||
if (t instanceof FizzRuntimeException) {
|
||||
FizzRuntimeException ex = (FizzRuntimeException) t;
|
||||
// log.error(traceId + ' ' + tMsg, LogService.BIZ_ID, traceId, ex);
|
||||
org.apache.logging.log4j.ThreadContext.put(Consts.TRACE_ID, traceId);
|
||||
log.error(traceId + ' ' + tMsg, ex);
|
||||
LOGGER.error(tMsg, ex);
|
||||
respHeaders.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
|
||||
RespEntity rs = null;
|
||||
if (ex.getStepContext() != null && ex.getStepContext().returnContext()) {
|
||||
@@ -132,9 +137,8 @@ public class FilterExceptionHandlerConfig {
|
||||
if (fc == null) { // t came from flow control filter
|
||||
StringBuilder b = ThreadContext.getStringBuilder();
|
||||
WebUtils.request2stringBuilder(exchange, b);
|
||||
// log.error(b.toString(), LogService.BIZ_ID, traceId, t);
|
||||
org.apache.logging.log4j.ThreadContext.put(Consts.TRACE_ID, traceId);
|
||||
log.error(b.toString(), t);
|
||||
LOGGER.error(b.toString(), t);
|
||||
String s = WebUtils.jsonRespBody(HttpStatus.INTERNAL_SERVER_ERROR.value(), tMsg, traceId);
|
||||
respHeaders.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
|
||||
vm = resp.writeWith(Mono.just(resp.bufferFactory().wrap(s.getBytes())));
|
||||
|
||||
@@ -51,14 +51,14 @@ public class FizzLogFilter implements WebFilter {
|
||||
long start = System.currentTimeMillis();
|
||||
return chain.filter(exchange).doFinally(
|
||||
(c) -> {
|
||||
if (log.isInfoEnabled()) {
|
||||
if (log.isDebugEnabled()) {
|
||||
StringBuilder b = ThreadContext.getStringBuilder();
|
||||
WebUtils.request2stringBuilder(exchange, b);
|
||||
b.append(resp).append(exchange.getResponse().getStatusCode())
|
||||
.append(in) .append(System.currentTimeMillis() - start);
|
||||
// log.info(b.toString(), LogService.BIZ_ID, WebUtils.getTraceId(exchange));
|
||||
org.apache.logging.log4j.ThreadContext.put(Consts.TRACE_ID, WebUtils.getTraceId(exchange));
|
||||
log.info(b.toString());
|
||||
log.debug(b.toString());
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@@ -17,19 +17,6 @@
|
||||
|
||||
package com.fizzgate.filter;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebFilterChain;
|
||||
|
||||
import com.fizzgate.config.SystemConfig;
|
||||
import com.fizzgate.monitor.FizzMonitorService;
|
||||
import com.fizzgate.plugin.auth.ApiConfigService;
|
||||
@@ -44,7 +31,19 @@ import com.fizzgate.stats.degrade.DegradeRule;
|
||||
import com.fizzgate.stats.ratelimit.ResourceRateLimitConfig;
|
||||
import com.fizzgate.stats.ratelimit.ResourceRateLimitConfigService;
|
||||
import com.fizzgate.util.*;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.reactive.function.client.ClientResponse;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebFilterChain;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.core.publisher.SignalType;
|
||||
|
||||
@@ -63,7 +62,7 @@ public class FlowControlFilter extends FizzWebFilter {
|
||||
|
||||
public static final String FLOW_CONTROL_FILTER = "flowControlFilter";
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(FlowControlFilter.class);
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(FlowControlFilter.class);
|
||||
|
||||
private static final String admin = "admin";
|
||||
|
||||
@@ -160,7 +159,6 @@ public class FlowControlFilter extends FizzWebFilter {
|
||||
|
||||
if (!favReq && flowControlFilterProperties.isFlowControl() && !adminReq && !proxyTestReq && !fizzApiReq) {
|
||||
String traceId = WebUtils.getTraceId(exchange);
|
||||
// LogService.setBizId(traceId);
|
||||
org.apache.logging.log4j.ThreadContext.put(Consts.TRACE_ID, traceId);
|
||||
if (!apiConfigService.serviceConfigMap.containsKey(service)) {
|
||||
String json = WebUtils.jsonRespBody(HttpStatus.FORBIDDEN.value(), "no service " + service + " in flow config", traceId);
|
||||
@@ -178,12 +176,10 @@ public class FlowControlFilter extends FizzWebFilter {
|
||||
});
|
||||
|
||||
if (result != null && !result.isSuccess()) {
|
||||
long currentTimeMillis = System.currentTimeMillis();
|
||||
String blockedResourceId = result.getBlockedResourceId();
|
||||
if (BlockType.CIRCUIT_BREAK == result.getBlockType()) {
|
||||
fizzMonitorService.alarm(service, path, FizzMonitorService.CIRCUIT_BREAK_ALARM, null);
|
||||
// log.info("{} trigger {} circuit breaker limit", traceId, blockedResourceId, LogService.BIZ_ID, traceId);
|
||||
log.info("{} trigger {} circuit breaker limit", traceId, blockedResourceId);
|
||||
LOGGER.info("{} trigger {} circuit breaker limit", traceId, blockedResourceId);
|
||||
|
||||
String responseContentType = flowControlFilterProperties.getDegradeDefaultResponseContentType();
|
||||
String responseContent = flowControlFilterProperties.getDegradeDefaultResponseContent();
|
||||
@@ -214,12 +210,10 @@ public class FlowControlFilter extends FizzWebFilter {
|
||||
} else {
|
||||
if (BlockType.CONCURRENT_REQUEST == result.getBlockType()) {
|
||||
fizzMonitorService.alarm(service, path, FizzMonitorService.RATE_LIMIT_ALARM, concurrents);
|
||||
// log.info("{} exceed {} flow limit, blocked by maximum concurrent requests", traceId, blockedResourceId, LogService.BIZ_ID, traceId);
|
||||
log.info("{} exceed {} flow limit, blocked by maximum concurrent requests", traceId, blockedResourceId);
|
||||
LOGGER.info("{} exceed {} flow limit, blocked by maximum concurrent requests", traceId, blockedResourceId);
|
||||
} else {
|
||||
fizzMonitorService.alarm(service, path, FizzMonitorService.RATE_LIMIT_ALARM, qps);
|
||||
// log.info("{} exceed {} flow limit, blocked by maximum QPS", traceId, blockedResourceId, LogService.BIZ_ID, traceId);
|
||||
log.info("{} exceed {} flow limit, blocked by maximum QPS", traceId, blockedResourceId);
|
||||
LOGGER.info("{} exceed {} flow limit, blocked by maximum QPS", traceId, blockedResourceId);
|
||||
}
|
||||
|
||||
ResourceRateLimitConfig c = resourceRateLimitConfigService.getResourceRateLimitConfig(ResourceIdUtils.NODE_RESOURCE);
|
||||
@@ -241,10 +235,11 @@ public class FlowControlFilter extends FizzWebFilter {
|
||||
}
|
||||
} else {
|
||||
long start = System.currentTimeMillis();
|
||||
setTraceId(exchange);
|
||||
String finalService = service;
|
||||
String finalPath = path;
|
||||
return chain.filter(exchange).doFinally(s -> {
|
||||
|
||||
org.apache.logging.log4j.ThreadContext.put(Consts.TRACE_ID, traceId);
|
||||
long rt = System.currentTimeMillis() - start;
|
||||
CircuitBreaker cb = exchange.getAttribute(CircuitBreaker.DETECT_REQUEST);
|
||||
HttpStatus statusCode = exchange.getResponse().getStatusCode();
|
||||
@@ -252,7 +247,7 @@ public class FlowControlFilter extends FizzWebFilter {
|
||||
if (t instanceof TimeoutException) {
|
||||
statusCode = HttpStatus.GATEWAY_TIMEOUT;
|
||||
}
|
||||
// if (s == SignalType.ON_ERROR || statusCode.is4xxClientError() || statusCode.is5xxServerError()) {
|
||||
|
||||
if (s == SignalType.ON_ERROR || statusCode.is5xxServerError()) {
|
||||
flowStat.addRequestRT(resourceConfigs, currentTimeSlot, rt, false, statusCode);
|
||||
if (cb != null) {
|
||||
@@ -271,11 +266,18 @@ public class FlowControlFilter extends FizzWebFilter {
|
||||
cb.transit(CircuitBreaker.State.RESUME_DETECTIVE, CircuitBreaker.State.CLOSED, currentTimeSlot, flowStat);
|
||||
}
|
||||
}
|
||||
|
||||
if (s == SignalType.CANCEL) {
|
||||
ClientResponse remoteResp = exchange.getAttribute("remoteResp");
|
||||
if (remoteResp != null) {
|
||||
remoteResp.bodyToMono(Void.class).subscribe();
|
||||
LOGGER.warn("client cancel, and dispose remote response");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// setTraceId(exchange);
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
@@ -340,15 +342,15 @@ public class FlowControlFilter extends FizzWebFilter {
|
||||
check = true;
|
||||
}
|
||||
}
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("getResourceConfigItselfAndParents:\n" + JacksonUtils.writeValueAsString(rc) + '\n' + JacksonUtils.writeValueAsString(result));
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("getResourceConfigItselfAndParents:\n" + JacksonUtils.writeValueAsString(rc) + '\n' + JacksonUtils.writeValueAsString(result));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<ResourceConfig> getFlowControlConfigs(String app, String ip, String node, String service, String path) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("get flow control configs by app={}, ip={}, node={}, service={}, path={}", app, ip, node, service, path);
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("get flow control configs by app={}, ip={}, node={}, service={}, path={}", app, ip, node, service, path);
|
||||
}
|
||||
boolean hasHost = (StringUtils.isNotBlank(node) && !node.equals(ResourceIdUtils.NODE));
|
||||
int sz = hasHost ? 10 : 9;
|
||||
@@ -377,8 +379,8 @@ public class FlowControlFilter extends FizzWebFilter {
|
||||
checkRateLimitConfigAndAddTo(resourceConfigs, b, null, ip, null, service, path, null);
|
||||
}
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("resource configs: " + JacksonUtils.writeValueAsString(resourceConfigs));
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("resource configs: " + JacksonUtils.writeValueAsString(resourceConfigs));
|
||||
}
|
||||
return resourceConfigs;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
package com.fizzgate.filter;
|
||||
|
||||
import com.fizzgate.util.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.annotation.Order;
|
||||
@@ -35,10 +36,6 @@ import com.fizzgate.plugin.auth.GatewayGroup;
|
||||
import com.fizzgate.plugin.auth.GatewayGroupService;
|
||||
import com.fizzgate.plugin.stat.StatPluginFilter;
|
||||
import com.fizzgate.proxy.Route;
|
||||
import com.fizzgate.util.ReactorUtils;
|
||||
import com.fizzgate.util.Result;
|
||||
import com.fizzgate.util.ThreadContext;
|
||||
import com.fizzgate.util.WebUtils;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@@ -54,7 +51,7 @@ import java.util.function.Function;
|
||||
@Order(10)
|
||||
public class PreprocessFilter extends FizzWebFilter {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(PreprocessFilter.class);
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(PreprocessFilter.class);
|
||||
|
||||
public static final String PREPROCESS_FILTER = "preprocessFilter";
|
||||
|
||||
@@ -72,6 +69,12 @@ public class PreprocessFilter extends FizzWebFilter {
|
||||
@Override
|
||||
public Mono<Void> doFilter(ServerWebExchange exchange, WebFilterChain chain) {
|
||||
|
||||
String traceId = WebUtils.getTraceId(exchange);
|
||||
org.apache.logging.log4j.ThreadContext.put(Consts.TRACE_ID, traceId);
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("preprocess filter start");
|
||||
}
|
||||
|
||||
Map<String, FilterResult> fc = new HashMap<>(); fc.put(WebUtils.PREV_FILTER_RESULT, succFr);
|
||||
Map<String, String> appendHdrs = new HashMap<>(8);
|
||||
Map<String, Object> eas = exchange.getAttributes(); eas.put(WebUtils.FILTER_CONTEXT, fc);
|
||||
@@ -85,8 +88,14 @@ public class PreprocessFilter extends FizzWebFilter {
|
||||
.thenReturn(ReactorUtils.Void)
|
||||
.flatMap(
|
||||
v -> {
|
||||
|
||||
org.apache.logging.log4j.ThreadContext.put(Consts.TRACE_ID, traceId);
|
||||
|
||||
Result<ApiConfig> auth = (Result<ApiConfig>) WebUtils.getFilterResultDataItem(exchange, AuthPluginFilter.AUTH_PLUGIN_FILTER, AuthPluginFilter.RESULT);
|
||||
if (auth.code == Result.FAIL) {
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("preprocess filter end 403");
|
||||
}
|
||||
return WebUtils.responseError(exchange, HttpStatus.FORBIDDEN.value(), auth.msg);
|
||||
}
|
||||
ApiConfig ac = auth.data;
|
||||
|
||||
@@ -17,11 +17,22 @@
|
||||
|
||||
package com.fizzgate.filter;
|
||||
|
||||
import com.fizzgate.config.SystemConfig;
|
||||
import com.fizzgate.plugin.auth.ApiConfig;
|
||||
import com.fizzgate.proxy.FizzWebClient;
|
||||
import com.fizzgate.proxy.Route;
|
||||
import com.fizzgate.proxy.dubbo.ApacheDubboGenericService;
|
||||
import com.fizzgate.proxy.dubbo.DubboInterfaceDeclaration;
|
||||
import com.fizzgate.service_registry.RegistryCenterService;
|
||||
import com.fizzgate.stats.FlowStat;
|
||||
import com.fizzgate.stats.ResourceConfig;
|
||||
import com.fizzgate.util.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.core.io.buffer.DataBufferUtils;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
@@ -32,22 +43,13 @@ import org.springframework.web.reactive.function.BodyExtractors;
|
||||
import org.springframework.web.reactive.function.client.ClientResponse;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebFilterChain;
|
||||
|
||||
import com.fizzgate.config.SystemConfig;
|
||||
import com.fizzgate.plugin.auth.ApiConfig;
|
||||
import com.fizzgate.proxy.FizzWebClient;
|
||||
import com.fizzgate.proxy.Route;
|
||||
import com.fizzgate.proxy.dubbo.ApacheDubboGenericService;
|
||||
import com.fizzgate.proxy.dubbo.DubboInterfaceDeclaration;
|
||||
import com.fizzgate.service_registry.RegistryCenterService;
|
||||
import com.fizzgate.util.*;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author hongqiaowei
|
||||
@@ -57,7 +59,7 @@ import java.util.function.Function;
|
||||
@Order(Ordered.LOWEST_PRECEDENCE)
|
||||
public class RouteFilter extends FizzWebFilter {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(RouteFilter.class);
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(RouteFilter.class);
|
||||
|
||||
@Resource
|
||||
private FizzWebClient fizzWebClient;
|
||||
@@ -68,6 +70,9 @@ public class RouteFilter extends FizzWebFilter {
|
||||
@Resource
|
||||
private SystemConfig systemConfig;
|
||||
|
||||
@Resource
|
||||
private FlowControlFilter flowControlFilter;
|
||||
|
||||
@Override
|
||||
public Mono<Void> doFilter(ServerWebExchange exchange, WebFilterChain chain) {
|
||||
|
||||
@@ -77,16 +82,13 @@ public class RouteFilter extends FizzWebFilter {
|
||||
} else {
|
||||
Mono<Void> resp = WebUtils.getDirectResponse(exchange);
|
||||
if (resp == null) { // should not reach here
|
||||
ServerHttpRequest clientReq = exchange.getRequest();
|
||||
String traceId = WebUtils.getTraceId(exchange);
|
||||
org.apache.logging.log4j.ThreadContext.put(Consts.TRACE_ID, traceId);
|
||||
String msg = traceId + ' ' + pfr.id + " fail";
|
||||
if (pfr.cause == null) {
|
||||
// log.error(msg, LogService.BIZ_ID, traceId);
|
||||
log.error(msg);
|
||||
LOGGER.error(msg);
|
||||
} else {
|
||||
// log.error(msg, LogService.BIZ_ID, traceId, pfr.cause);
|
||||
log.error(msg, pfr.cause);
|
||||
LOGGER.error(msg, pfr.cause);
|
||||
}
|
||||
HttpStatus s = HttpStatus.INTERNAL_SERVER_ERROR;
|
||||
if (!SystemConfig.FIZZ_ERR_RESP_HTTP_STATUS_ENABLE) {
|
||||
@@ -101,8 +103,13 @@ public class RouteFilter extends FizzWebFilter {
|
||||
|
||||
private Mono<Void> doFilter0(ServerWebExchange exchange, WebFilterChain chain) {
|
||||
|
||||
ServerHttpRequest req = exchange.getRequest();
|
||||
String traceId = WebUtils.getTraceId(exchange);
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
org.apache.logging.log4j.ThreadContext.put(Consts.TRACE_ID, traceId);
|
||||
LOGGER.debug("route filter start");
|
||||
}
|
||||
|
||||
ServerHttpRequest req = exchange.getRequest();
|
||||
Route route = exchange.getAttribute(WebUtils.ROUTE);
|
||||
HttpHeaders hdrs = null;
|
||||
|
||||
@@ -112,32 +119,17 @@ public class RouteFilter extends FizzWebFilter {
|
||||
}
|
||||
|
||||
if (route == null) {
|
||||
/*String pathQuery = WebUtils.getClientReqPathQuery(exchange);
|
||||
return fizzWebClient.send2service(traceId, req.getMethod(), WebUtils.getClientService(exchange), pathQuery, hdrs, req.getBody(), 0, 0, 0)
|
||||
.flatMap(genServerResponse(exchange));*/
|
||||
|
||||
Map.Entry<String, List<String>> pathQueryTemplate = WebUtils.getClientReqPathQueryTemplate(exchange).entrySet().iterator().next();
|
||||
return fizzWebClient.send2service(traceId, req.getMethod(), WebUtils.getClientService(exchange), pathQueryTemplate.getKey(), hdrs, req.getBody(), 0, 0, 0, pathQueryTemplate.getValue().toArray(new String[0]))
|
||||
.flatMap(genServerResponse(exchange));
|
||||
|
||||
} else if (route.type == ApiConfig.Type.SERVICE_DISCOVERY) {
|
||||
/*String pathQuery = getBackendPathQuery(req, route);
|
||||
String svc = RegistryCenterService.getServiceNameSpace(route.registryCenter, route.backendService);
|
||||
return fizzWebClient.send2service(traceId, route.method, svc, pathQuery, hdrs, req.getBody(), route.timeout, route.retryCount, route.retryInterval)
|
||||
.flatMap(genServerResponse(exchange));*/
|
||||
|
||||
Map.Entry<String, List<String>> pathQueryTemplate = getBackendPathQueryTemplate(req, route).entrySet().iterator().next();
|
||||
String svc = RegistryCenterService.getServiceNameSpace(route.registryCenter, route.backendService);
|
||||
return fizzWebClient.send2service(traceId, route.method, svc, pathQueryTemplate.getKey(), hdrs, req.getBody(), route.timeout, route.retryCount, route.retryInterval, pathQueryTemplate.getValue().toArray(new String[0]))
|
||||
.flatMap(genServerResponse(exchange));
|
||||
|
||||
} else if (route.type == ApiConfig.Type.REVERSE_PROXY) {
|
||||
/*String uri = ThreadContext.getStringBuilder().append(route.nextHttpHostPort)
|
||||
.append(getBackendPathQuery(req, route))
|
||||
.toString();
|
||||
return fizzWebClient.send(traceId, route.method, uri, hdrs, req.getBody(), route.timeout, route.retryCount, route.retryInterval)
|
||||
.flatMap(genServerResponse(exchange));*/
|
||||
|
||||
Map.Entry<String, List<String>> pathQueryTemplate = getBackendPathQueryTemplate(req, route).entrySet().iterator().next();
|
||||
String uri = ThreadContext.getStringBuilder().append(route.nextHttpHostPort)
|
||||
.append(pathQueryTemplate.getKey())
|
||||
@@ -150,20 +142,6 @@ public class RouteFilter extends FizzWebFilter {
|
||||
}
|
||||
}
|
||||
|
||||
private String getBackendPathQuery(ServerHttpRequest request, Route route) {
|
||||
String qry = route.query;
|
||||
if (qry == null) {
|
||||
MultiValueMap<String, String> queryParams = request.getQueryParams();
|
||||
if (queryParams.isEmpty()) {
|
||||
return route.backendPath;
|
||||
} else {
|
||||
return route.backendPath + Consts.S.QUESTION + WebUtils.toQueryString(queryParams);
|
||||
}
|
||||
} else {
|
||||
return route.backendPath + Consts.S.QUESTION + qry;
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, List<String>> getBackendPathQueryTemplate(ServerHttpRequest request, Route route) {
|
||||
String qry = route.query;
|
||||
if (qry == null) {
|
||||
@@ -183,6 +161,7 @@ public class RouteFilter extends FizzWebFilter {
|
||||
|
||||
private Function<ClientResponse, Mono<? extends Void>> genServerResponse(ServerWebExchange exchange) {
|
||||
return remoteResp -> {
|
||||
String traceId = WebUtils.getTraceId(exchange);
|
||||
ServerHttpResponse clientResp = exchange.getResponse();
|
||||
clientResp.setStatusCode(remoteResp.statusCode());
|
||||
HttpHeaders clientRespHeaders = clientResp.getHeaders();
|
||||
@@ -203,24 +182,36 @@ public class RouteFilter extends FizzWebFilter {
|
||||
}
|
||||
}
|
||||
);
|
||||
if (log.isDebugEnabled()) {
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
StringBuilder b = ThreadContext.getStringBuilder();
|
||||
String traceId = WebUtils.getTraceId(exchange);
|
||||
WebUtils.response2stringBuilder(traceId, remoteResp, b);
|
||||
// log.debug(b.toString(), LogService.BIZ_ID, traceId);
|
||||
org.apache.logging.log4j.ThreadContext.put(Consts.TRACE_ID, traceId);
|
||||
log.debug(b.toString());
|
||||
LOGGER.debug(b.toString());
|
||||
}
|
||||
return clientResp.writeWith(remoteResp.body(BodyExtractors.toDataBuffers()))
|
||||
.doOnError(throwable -> cleanup(remoteResp)).doOnCancel(() -> cleanup(remoteResp));
|
||||
.doOnError(
|
||||
t -> {
|
||||
org.apache.logging.log4j.ThreadContext.put(Consts.TRACE_ID, traceId);
|
||||
exchange.getAttributes().put("remoteResp", remoteResp);
|
||||
LOGGER.error("response client error", t);
|
||||
}
|
||||
)
|
||||
.doOnCancel(
|
||||
() -> {
|
||||
org.apache.logging.log4j.ThreadContext.put(Consts.TRACE_ID, traceId);
|
||||
exchange.getAttributes().put("remoteResp", remoteResp);
|
||||
LOGGER.error("client signal cancel");
|
||||
// cleanup(remoteResp);
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
private void cleanup(ClientResponse clientResponse) {
|
||||
if (clientResponse != null) {
|
||||
clientResponse.bodyToMono(Void.class).subscribe();
|
||||
}
|
||||
}
|
||||
// private void cleanup(ClientResponse clientResponse) {
|
||||
// if (clientResponse != null) {
|
||||
// clientResponse.bodyToMono(Void.class).subscribe();
|
||||
// }
|
||||
// }
|
||||
|
||||
private Mono<Void> dubboRpc(ServerWebExchange exchange, Route route) {
|
||||
final String[] ls = {null};
|
||||
@@ -272,9 +263,8 @@ public class RouteFilter extends FizzWebFilter {
|
||||
if (ls[0] != null) {
|
||||
b.append('\n').append(ls[0]);
|
||||
}
|
||||
// log.error(b.toString(), LogService.BIZ_ID, WebUtils.getTraceId(exchange), t);
|
||||
org.apache.logging.log4j.ThreadContext.put(Consts.TRACE_ID, WebUtils.getTraceId(exchange));
|
||||
log.error(b.toString(), t);
|
||||
LOGGER.error(b.toString(), t);
|
||||
}
|
||||
)
|
||||
;
|
||||
|
||||
@@ -27,6 +27,8 @@ import javax.crypto.spec.DESKeySpec;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
import org.apache.commons.codec.digest.HmacAlgorithms;
|
||||
import org.apache.commons.codec.digest.HmacUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -77,6 +79,12 @@ public class CodecFunc implements IFunc {
|
||||
FuncExecutor.register(NAME_SPACE_PREFIX + "codec.aesDecrypt", this);
|
||||
FuncExecutor.register(NAME_SPACE_PREFIX + "codec.desEncrypt", this);
|
||||
FuncExecutor.register(NAME_SPACE_PREFIX + "codec.desDecrypt", this);
|
||||
FuncExecutor.register(NAME_SPACE_PREFIX + "codec.hmacMd5", this);
|
||||
FuncExecutor.register(NAME_SPACE_PREFIX + "codec.hmacSha1", this);
|
||||
FuncExecutor.register(NAME_SPACE_PREFIX + "codec.hmacSha224", this);
|
||||
FuncExecutor.register(NAME_SPACE_PREFIX + "codec.hmacSha256", this);
|
||||
FuncExecutor.register(NAME_SPACE_PREFIX + "codec.hmacSha384", this);
|
||||
FuncExecutor.register(NAME_SPACE_PREFIX + "codec.hmacSha512", this);
|
||||
}
|
||||
|
||||
public String md5(String data) {
|
||||
@@ -181,4 +189,28 @@ public class CodecFunc implements IFunc {
|
||||
}
|
||||
}
|
||||
|
||||
public String hmacMd5(String data, String secretKey) {
|
||||
return new HmacUtils(HmacAlgorithms.HMAC_MD5, secretKey).hmacHex(data);
|
||||
}
|
||||
|
||||
public String hmacSha1(String data, String secretKey) {
|
||||
return new HmacUtils(HmacAlgorithms.HMAC_SHA_1, secretKey).hmacHex(data);
|
||||
}
|
||||
|
||||
public String hmacSha224(String data, String secretKey) {
|
||||
return new HmacUtils(HmacAlgorithms.HMAC_SHA_224, secretKey).hmacHex(data);
|
||||
}
|
||||
|
||||
public String hmacSha256(String data, String secretKey) {
|
||||
return new HmacUtils(HmacAlgorithms.HMAC_SHA_256, secretKey).hmacHex(data);
|
||||
}
|
||||
|
||||
public String hmacSha384(String data, String secretKey) {
|
||||
return new HmacUtils(HmacAlgorithms.HMAC_SHA_384, secretKey).hmacHex(data);
|
||||
}
|
||||
|
||||
public String hmacSha512(String data, String secretKey) {
|
||||
return new HmacUtils(HmacAlgorithms.HMAC_SHA_512, secretKey).hmacHex(data);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,6 +17,9 @@
|
||||
|
||||
package com.fizzgate.plugin;
|
||||
|
||||
import com.fizzgate.util.Consts;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebFilterChain;
|
||||
|
||||
@@ -38,6 +41,8 @@ import java.util.Map;
|
||||
|
||||
public final class FizzPluginFilterChain {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(FizzPluginFilterChain.class);
|
||||
|
||||
private static final String pluginConfigsIt = "pcsit@";
|
||||
|
||||
public static final String WEB_FILTER_CHAIN = "wfc@";
|
||||
@@ -62,6 +67,13 @@ public final class FizzPluginFilterChain {
|
||||
if (it.hasNext()) {
|
||||
PluginConfig pc = it.next();
|
||||
FizzPluginFilter pf = Fizz.context.getBean(pc.plugin, FizzPluginFilter.class);
|
||||
|
||||
String traceId = WebUtils.getTraceId(exchange);
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
org.apache.logging.log4j.ThreadContext.put(Consts.TRACE_ID, traceId);
|
||||
LOGGER.debug("{} start", pc.plugin);
|
||||
}
|
||||
|
||||
Mono<Void> m = pf.filter(exchange, pc.config);
|
||||
if (pf instanceof PluginFilter) {
|
||||
boolean f = false;
|
||||
|
||||
@@ -43,7 +43,7 @@ import java.util.Map;
|
||||
@Deprecated
|
||||
public abstract class PluginFilter implements FizzPluginFilter {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(PluginFilter.class);
|
||||
private final Logger LOGGER = LoggerFactory.getLogger(getClass());
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, Map<String, Object> config) {
|
||||
@@ -55,9 +55,8 @@ public abstract class PluginFilter implements FizzPluginFilter {
|
||||
FilterResult pfr = WebUtils.getPrevFilterResult(exchange);
|
||||
String traceId = WebUtils.getTraceId(exchange);
|
||||
ThreadContext.put(Consts.TRACE_ID, traceId);
|
||||
if (log.isDebugEnabled()) {
|
||||
// log.debug(traceId + ' ' + this + ": " + pfr.id + " execute " + (pfr.success ? "success" : "fail"), LogService.BIZ_ID, traceId);
|
||||
log.debug(traceId + ' ' + this + ": " + pfr.id + " execute " + (pfr.success ? "success" : "fail"));
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("{} execute {}", pfr.id, pfr.success ? "success" : "fail");
|
||||
}
|
||||
if (pfr.success) {
|
||||
return doFilter(exchange, config, fixedConfig);
|
||||
@@ -65,9 +64,9 @@ public abstract class PluginFilter implements FizzPluginFilter {
|
||||
if (WebUtils.getDirectResponse(exchange) == null) { // should not reach here
|
||||
String msg = traceId + ' ' + pfr.id + " fail";
|
||||
if (pfr.cause == null) {
|
||||
log.error(msg);
|
||||
LOGGER.error(msg);
|
||||
} else {
|
||||
log.error(msg, pfr.cause);
|
||||
LOGGER.error(msg, pfr.cause);
|
||||
}
|
||||
HttpStatus s = HttpStatus.OK;
|
||||
if (SystemConfig.FIZZ_ERR_RESP_HTTP_STATUS_ENABLE) {
|
||||
|
||||
@@ -56,7 +56,6 @@ public class AuthPluginFilter extends PluginFilter {
|
||||
r -> {
|
||||
if (log.isDebugEnabled()) {
|
||||
String traceId = WebUtils.getTraceId(exchange);
|
||||
// log.debug("{} req auth: {}", traceId, r, LogService.BIZ_ID, traceId);
|
||||
ThreadContext.put(Consts.TRACE_ID, traceId);
|
||||
log.debug("{} req auth: {}", traceId, r);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
package com.fizzgate.plugin.auth;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fizzgate.util.Consts;
|
||||
import com.fizzgate.util.ThreadContext;
|
||||
import com.fizzgate.util.UrlTransformUtils;
|
||||
|
||||
@@ -124,18 +125,22 @@ public class ServiceConfig {
|
||||
}
|
||||
|
||||
private void checkPathPattern(Map<String, Set<ApiConfig>> pathPattern2apiConfigMap, boolean dedicatedLineRequest, String path, ArrayList<ApiConfig> result) {
|
||||
String path0 = path;
|
||||
if (!path.equals(Consts.S.FORWARD_SLASH_STR)) {
|
||||
int lastCharPos = path.length() - 1;
|
||||
char c = path.charAt(lastCharPos);
|
||||
if (c == Consts.S.FORWARD_SLASH) {
|
||||
path0 = path.substring(0, lastCharPos);
|
||||
} else {
|
||||
path0 = path + Consts.S.FORWARD_SLASH;
|
||||
}
|
||||
}
|
||||
Set<Map.Entry<String, Set<ApiConfig>>> entries = pathPattern2apiConfigMap.entrySet();
|
||||
// boolean clear = false;
|
||||
for (Map.Entry<String, Set<ApiConfig>> entry : entries) {
|
||||
String pathPattern = entry.getKey();
|
||||
Set<ApiConfig> apiConfigs = entry.getValue();
|
||||
if (pathPattern.equals(path)) {
|
||||
if (pathPattern.equals(path) || pathPattern.equals(path0)) {
|
||||
for (ApiConfig ac : apiConfigs) {
|
||||
// if (ac.allowAccess) {
|
||||
/*if (!clear && !result.isEmpty()) {
|
||||
result.clear();
|
||||
clear = true;
|
||||
}*/
|
||||
if (dedicatedLineRequest) {
|
||||
if (ac.dedicatedLine) {
|
||||
result.add(ac);
|
||||
@@ -145,14 +150,9 @@ public class ServiceConfig {
|
||||
result.add(ac);
|
||||
}
|
||||
}
|
||||
// }
|
||||
}
|
||||
/*if (clear && !result.isEmpty()) {
|
||||
return;
|
||||
}*/
|
||||
} else if (UrlTransformUtils.ANT_PATH_MATCHER.match(pathPattern, path)) {
|
||||
} else if (UrlTransformUtils.ANT_PATH_MATCHER.match(pathPattern, path) || UrlTransformUtils.ANT_PATH_MATCHER.match(pathPattern, path0)) {
|
||||
for (ApiConfig ac : apiConfigs) {
|
||||
// if (ac.allowAccess) {
|
||||
if (dedicatedLineRequest) {
|
||||
if (ac.dedicatedLine) {
|
||||
result.add(ac);
|
||||
@@ -162,9 +162,8 @@ public class ServiceConfig {
|
||||
result.add(ac);
|
||||
}
|
||||
}
|
||||
// }
|
||||
}
|
||||
}
|
||||
} // end for
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,13 +46,19 @@ import java.util.Map;
|
||||
@Component(RequestBodyPlugin.REQUEST_BODY_PLUGIN)
|
||||
public class RequestBodyPlugin implements FizzPluginFilter {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(RequestBodyPlugin.class);
|
||||
private final Logger LOGGER = LoggerFactory.getLogger(getClass());
|
||||
|
||||
public static final String REQUEST_BODY_PLUGIN = "requestBodyPlugin";
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, Map<String, Object> config) {
|
||||
|
||||
// String traceId = WebUtils.getTraceId(exchange);
|
||||
// if (LOGGER.isDebugEnabled()) {
|
||||
// org.apache.logging.log4j.ThreadContext.put(Consts.TRACE_ID, traceId);
|
||||
// LOGGER.debug("{} start", getClass().getSimpleName());
|
||||
// }
|
||||
|
||||
ServerHttpRequest req = exchange.getRequest();
|
||||
if (req instanceof FizzServerHttpRequestDecorator) {
|
||||
return doFilter(exchange, config);
|
||||
@@ -76,18 +82,21 @@ public class RequestBodyPlugin implements FizzPluginFilter {
|
||||
if (MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith(contentType)) {
|
||||
newExchange = new FizzServerWebExchangeDecorator(mutatedExchange);
|
||||
}
|
||||
if (log.isDebugEnabled()) {
|
||||
String traceId = WebUtils.getTraceId(exchange);
|
||||
// log.debug("{} request is decorated", traceId, LogService.BIZ_ID, traceId);
|
||||
ThreadContext.put(Consts.TRACE_ID, traceId);
|
||||
log.debug("{} request is decorated", traceId);
|
||||
}
|
||||
// if (LOGGER.isDebugEnabled()) {
|
||||
// ThreadContext.put(Consts.TRACE_ID, traceId);
|
||||
// LOGGER.debug("{} request is decorated", traceId);
|
||||
// }
|
||||
return doFilter(newExchange, config);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public Mono<Void> doFilter(ServerWebExchange exchange, Map<String, Object> config) {
|
||||
String traceId = WebUtils.getTraceId(exchange);
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
org.apache.logging.log4j.ThreadContext.put(Consts.TRACE_ID, traceId);
|
||||
LOGGER.debug("{} end", getClass().getSimpleName());
|
||||
}
|
||||
return FizzPluginFilterChain.next(exchange);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,6 +90,7 @@ public class StatPluginFilter extends PluginFilter {
|
||||
accessStat.reqTime = System.currentTimeMillis();
|
||||
accessStat.reqs++;
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
org.apache.logging.log4j.ThreadContext.put(Consts.TRACE_ID, WebUtils.getTraceId(exchange));
|
||||
LOGGER.debug("update access stat: {}, which request at {}", accessStat, DateTimeUtils.convert(accessStat.reqTime, Consts.DP.DP19));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,7 +220,6 @@ public class FizzWebClient {
|
||||
if (log.isDebugEnabled()) {
|
||||
StringBuilder b = ThreadContext.getStringBuilder();
|
||||
WebUtils.request2stringBuilder(traceId, method, uri, headers, null, b);
|
||||
// log.debug(b.toString(), LogService.BIZ_ID, traceId);
|
||||
org.apache.logging.log4j.ThreadContext.put(Consts.TRACE_ID, traceId);
|
||||
log.debug(b.toString());
|
||||
}
|
||||
@@ -243,10 +242,6 @@ public class FizzWebClient {
|
||||
}
|
||||
setHostHeader(uri, hdrs);
|
||||
if (systemConfig.isFizzWebClientXForwardedForEnable()) {
|
||||
List<String> values = hdrs.get(X_FORWARDED_FOR);
|
||||
/* if (CollectionUtils.isEmpty(values)) {
|
||||
hdrs.add(X_FORWARDED_FOR, WebUtils.getOriginIp(null));
|
||||
} */
|
||||
if (systemConfig.isFizzWebClientXForwardedForAppendGatewayIp()) {
|
||||
hdrs.add(X_FORWARDED_FOR, NetworkUtils.getServerIp());
|
||||
}
|
||||
@@ -269,8 +264,9 @@ public class FizzWebClient {
|
||||
|
||||
Mono<ClientResponse> cr = req.exchange();
|
||||
if (timeout == 0) {
|
||||
if (systemConfig.getRouteTimeout() != 0) {
|
||||
timeout = systemConfig.getRouteTimeout();
|
||||
long systemConfigRouteTimeout = systemConfig.getRouteTimeout();
|
||||
if (systemConfigRouteTimeout != 0) {
|
||||
timeout = systemConfigRouteTimeout;
|
||||
}
|
||||
}
|
||||
if (timeout > 0) {
|
||||
|
||||
@@ -17,8 +17,20 @@
|
||||
|
||||
package com.fizzgate.stats;
|
||||
|
||||
import java.util.*;
|
||||
import com.fizzgate.stats.circuitbreaker.CircuitBreakManager;
|
||||
import com.fizzgate.stats.circuitbreaker.CircuitBreaker;
|
||||
import com.fizzgate.util.Consts;
|
||||
import com.fizzgate.util.ResourceIdUtils;
|
||||
import com.fizzgate.util.WebUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
@@ -26,17 +38,7 @@ import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
import com.fizzgate.stats.circuitbreaker.CircuitBreakManager;
|
||||
import com.fizzgate.stats.circuitbreaker.CircuitBreaker;
|
||||
import com.fizzgate.util.ResourceIdUtils;
|
||||
import com.fizzgate.util.WebUtils;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Flow Statistic
|
||||
@@ -524,7 +526,7 @@ public class FlowStat {
|
||||
long slotInterval = slotIntervalInSec * 1000;
|
||||
|
||||
if (resourceId == null) {
|
||||
Set<Map.Entry<String, ResourceStat>> entrys = resourceStats.entrySet();
|
||||
Set<Entry<String, ResourceStat>> entrys = resourceStats.entrySet();
|
||||
for (Entry<String, ResourceStat> entry : entrys) {
|
||||
String rid = entry.getKey();
|
||||
ResourceTimeWindowStat resourceWin = new ResourceTimeWindowStat(rid);
|
||||
@@ -585,7 +587,7 @@ public class FlowStat {
|
||||
}
|
||||
}*/
|
||||
for (long i = lastSlotId; i < slotId;) {
|
||||
Set<Map.Entry<String, ResourceStat>> entrys = stat.resourceStats.entrySet();
|
||||
Set<Entry<String, ResourceStat>> entrys = stat.resourceStats.entrySet();
|
||||
for (Entry<String, ResourceStat> entry : entrys) {
|
||||
String resourceId = entry.getKey();
|
||||
ConcurrentMap<Long, TimeSlot> timeSlots = entry.getValue().getTimeSlots();
|
||||
@@ -649,7 +651,7 @@ public class FlowStat {
|
||||
long curTimeSlotId = stat.currentTimeSlotId();
|
||||
if (lastTimeSlotId == null || lastTimeSlotId.longValue() != curTimeSlotId) {
|
||||
// log.debug("PeakConcurrentJob start");
|
||||
Set<Map.Entry<String, ResourceStat>> entrys = stat.resourceStats.entrySet();
|
||||
Set<Entry<String, ResourceStat>> entrys = stat.resourceStats.entrySet();
|
||||
for (Entry<String, ResourceStat> entry : entrys) {
|
||||
String resource = entry.getKey();
|
||||
// log.debug("PeakConcurrentJob: resourceId={} slotId=={}", resourceId,
|
||||
|
||||
@@ -123,4 +123,11 @@ class CodecFuncTests {
|
||||
assertEquals("abc", result.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHmacSha256() {
|
||||
String funcExpression = "fn.codec.hmacSha256(\"12345678123456781234567812345678\", \"635e8562b968bc05bb80cacf124ebd53285280ee6845df0000faa33acafc38f0\")";
|
||||
Object result = FuncExecutor.getInstance().exec(null, funcExpression);
|
||||
assertEquals("c61be0237ec186df1c5f51425e607093b260a76e5de43a62cb3e821103303990", result.toString());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>fizz-gateway-community</artifactId>
|
||||
<groupId>com.fizzgate</groupId>
|
||||
<version>2.7.1</version>
|
||||
<version>2.7.2</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
148
fizz-plugin/src/main/java/com/fizzgate/plugin/ip/IpPlugin.java
Normal file
148
fizz-plugin/src/main/java/com/fizzgate/plugin/ip/IpPlugin.java
Normal file
@@ -0,0 +1,148 @@
|
||||
package com.fizzgate.plugin.ip;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fizzgate.plugin.ip.util.ConfigUtils;
|
||||
import com.fizzgate.plugin.ip.util.IpMatchUtils;
|
||||
import com.fizzgate.plugin.ip.util.IpUtils;
|
||||
import com.fizzgate.plugin.FizzPluginFilter;
|
||||
import com.fizzgate.plugin.FizzPluginFilterChain;
|
||||
import com.fizzgate.plugin.auth.ApiConfig;
|
||||
import com.fizzgate.plugin.auth.ApiConfigService;
|
||||
import com.fizzgate.util.WebUtils;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.net.SocketException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.fizzgate.plugin.ip.RouterConfig.FieldName.*;
|
||||
|
||||
/**
|
||||
* @author hua.huang
|
||||
*/
|
||||
@Slf4j
|
||||
@Component(value = IpPlugin.PLUGIN_NAME)
|
||||
public class IpPlugin implements FizzPluginFilter {
|
||||
public static final String PLUGIN_NAME = "fizz_plugin_ip";
|
||||
@Resource
|
||||
private ObjectMapper objectMapper;
|
||||
@Resource
|
||||
private ApiConfigService apiConfigService;
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Mono<Void> filter(ServerWebExchange exchange, Map<String, Object> config) {
|
||||
RouterConfig routerConfig = routerConfig(exchange, config);
|
||||
List<PluginConfig.Item> pluginConfigItemList = null;/* pluginConfig(exchange, config); */
|
||||
if (access(exchange, routerConfig, pluginConfigItemList)) {
|
||||
log.trace("pass...");
|
||||
return FizzPluginFilterChain.next(exchange);
|
||||
}
|
||||
log.trace("forbidden!");
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.set(HttpHeaders.CONTENT_TYPE, routerConfig.getErrorRespContentType());
|
||||
return WebUtils.buildDirectResponse(exchange, HttpStatus.FORBIDDEN,
|
||||
headers, routerConfig.getErrorRespContent());
|
||||
}
|
||||
|
||||
private boolean access(ServerWebExchange exchange,
|
||||
RouterConfig routerConfig, List<PluginConfig.Item> pluginConfigItemList) {
|
||||
Set<String> fixedWhiteIpSet = Sets.newHashSet();
|
||||
Set<String> fixedBlackIpSet = Sets.newHashSet();
|
||||
ApiConfig apiConfig = apiConfig(exchange);
|
||||
Set<String> gatewayGroups = (apiConfig == null || apiConfig.gatewayGroups == null) ? Sets.newHashSet() : apiConfig.gatewayGroups;
|
||||
/* if (!CollectionUtils.isEmpty(pluginConfigItemList)) {
|
||||
for (PluginConfig.Item fixedConfigItem : pluginConfigItemList) {
|
||||
if (gatewayGroups.contains(fixedConfigItem.getGwGroup())) {
|
||||
fixedWhiteIpSet.addAll(IpMatchUtils.ipConfigList(fixedConfigItem.getWhiteIp()));
|
||||
fixedBlackIpSet.addAll(IpMatchUtils.ipConfigList(fixedConfigItem.getBlackIp()));
|
||||
}
|
||||
}
|
||||
} */
|
||||
Set<String> whiteIpSet = ConfigUtils.string2set(routerConfig.getWhiteIp());
|
||||
Set<String> blackIpSet = ConfigUtils.string2set(routerConfig.getBlackIp());
|
||||
|
||||
String ip = null;
|
||||
try {
|
||||
ip = IpUtils.getServerHttpRequestIp(exchange.getRequest());
|
||||
} catch (SocketException e) {
|
||||
log.warn(e.getMessage(), e);
|
||||
}
|
||||
log.trace("clientIp:{}, fixedWhiteIpSet:{}, fixedBlackIpSet:{}, whiteIpSet:{}, blackIpSet:{}",
|
||||
ip, fixedWhiteIpSet, fixedBlackIpSet, whiteIpSet, blackIpSet);
|
||||
// 未获取到client ip,返回false
|
||||
if (StringUtils.isBlank(ip)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 优先匹配路由级别配置,然后再匹配插件级别配置
|
||||
|
||||
// 路由级别::白名单匹配到就直接返回true
|
||||
if (IpMatchUtils.match(ip, whiteIpSet)) {
|
||||
return true;
|
||||
}
|
||||
// 路由级别::黑名单匹配到就直接返回false
|
||||
if (IpMatchUtils.match(ip, blackIpSet)) {
|
||||
return false;
|
||||
}
|
||||
// 插件级别::白名单匹配到就直接返回true
|
||||
/* if (IpMatchUtils.match(ip, fixedWhiteIpSet)) {
|
||||
return true;
|
||||
} */
|
||||
// 插件级别::黑名单匹配到就直接返回false
|
||||
/* if (IpMatchUtils.match(ip, fixedBlackIpSet)) {
|
||||
return false;
|
||||
} */
|
||||
// 路由级别和插件级别都没匹配到
|
||||
if (CollectionUtils.isEmpty(whiteIpSet) /* || CollectionUtils.isEmpty(fixedWhiteIpSet) */) {
|
||||
// 都没有配置白名单,默认返回true
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private RouterConfig routerConfig(ServerWebExchange exchange, Map<String, Object> config) {
|
||||
RouterConfig routerConfig = new RouterConfig();
|
||||
routerConfig.setErrorRespContentType((String) config.getOrDefault(ERROR_RESP_CONTENT_TYPE
|
||||
, routerConfig.getErrorRespContentType()));
|
||||
routerConfig.setErrorRespContent((String) config.getOrDefault(ERROR_RESP_CONTENT
|
||||
, routerConfig.getErrorRespContent()));
|
||||
routerConfig.setWhiteIp((String) config.getOrDefault(WHITE_IP, StringUtils.EMPTY));
|
||||
routerConfig.setBlackIp((String) config.getOrDefault(BLACK_IP, StringUtils.EMPTY));
|
||||
return routerConfig;
|
||||
}
|
||||
|
||||
private List<PluginConfig.Item> pluginConfig(ServerWebExchange exchange, Map<String, Object> config) {
|
||||
String fixedConfig = (String) config.get(com.fizzgate.plugin.PluginConfig.CUSTOM_CONFIG);
|
||||
try {
|
||||
PluginConfig pluginConfig = objectMapper.readValue(fixedConfig, PluginConfig.class);
|
||||
if (pluginConfig != null && pluginConfig.getConfigs() != null) {
|
||||
return pluginConfig.getConfigs();
|
||||
}
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new RuntimeException(e.getMessage(), e);
|
||||
}
|
||||
return Lists.newArrayList();
|
||||
}
|
||||
|
||||
private ApiConfig apiConfig(ServerWebExchange exchange) {
|
||||
ServerHttpRequest req = exchange.getRequest();
|
||||
return apiConfigService.getApiConfig(WebUtils.getAppId(exchange),
|
||||
WebUtils.getClientService(exchange), req.getMethod(), WebUtils.getClientReqPath(exchange));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.fizzgate.plugin.ip;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class PluginConfig {
|
||||
private List<Item> configs = Lists.newArrayList();
|
||||
|
||||
@Data
|
||||
public static class Item {
|
||||
private String gwGroup;
|
||||
private String whiteIp;
|
||||
private String blackIp;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.fizzgate.plugin.ip;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.http.MediaType;
|
||||
|
||||
@Data
|
||||
public class RouterConfig {
|
||||
private String errorRespContentType = MediaType.APPLICATION_JSON_UTF8_VALUE;
|
||||
private String errorRespContent = "{\"msg\":\"非法IP\",\"code\":-1}";
|
||||
private String whiteIp;
|
||||
private String blackIp;
|
||||
|
||||
public interface FieldName {
|
||||
String ERROR_RESP_CONTENT_TYPE = "errorRespContentType";
|
||||
String ERROR_RESP_CONTENT = "errorRespContent";
|
||||
String WHITE_IP = "whiteIp";
|
||||
String BLACK_IP = "blackIp";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.fizzgate.plugin.ip.util;
|
||||
|
||||
import com.google.common.base.CharMatcher;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public abstract class ConfigUtils {
|
||||
public static final String DEFAULT_CHAR_MATCHER_ANY_OF = ",\n";
|
||||
|
||||
public static Set<String> string2set(String strVal, String charMatcherAnyOf) {
|
||||
Set<String> finalSet = Sets.newHashSet();
|
||||
if (StringUtils.isBlank(strVal)) {
|
||||
return finalSet;
|
||||
}
|
||||
charMatcherAnyOf = StringUtils.isBlank(charMatcherAnyOf) ? DEFAULT_CHAR_MATCHER_ANY_OF : charMatcherAnyOf;
|
||||
Set<String> set = Sets.newHashSet(
|
||||
Splitter.on(CharMatcher.anyOf(charMatcherAnyOf)).trimResults().split(strVal));
|
||||
set = set.stream().filter(StringUtils::isNotBlank).collect(Collectors.toSet());
|
||||
for (String s : set) {
|
||||
if (StringUtils.isBlank(s)) {
|
||||
continue;
|
||||
}
|
||||
finalSet.add(StringUtils.trimToEmpty(s));
|
||||
}
|
||||
return finalSet;
|
||||
}
|
||||
|
||||
public static Set<String> string2set(String strVal) {
|
||||
return string2set(strVal, DEFAULT_CHAR_MATCHER_ANY_OF);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,271 @@
|
||||
package com.fizzgate.plugin.ip.util;
|
||||
|
||||
import com.google.common.base.CharMatcher;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.Sets;
|
||||
import inet.ipaddr.AddressStringException;
|
||||
import inet.ipaddr.IPAddress;
|
||||
import inet.ipaddr.IPAddressString;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* ipv4匹配工具
|
||||
*/
|
||||
public final class IpMatchUtils {
|
||||
// IP正则
|
||||
private static final Pattern IP_PATTERN = Pattern.compile(
|
||||
"(1\\d{1,2}|2[0-4]\\d|25[0-5]|\\d{1,2})\\." + "(1\\d{1,2}|2[0-4]\\d|25[0-5]|\\d{1,2})\\."
|
||||
+ "(1\\d{1,2}|2[0-4]\\d|25[0-5]|\\d{1,2})\\." + "(1\\d{1,2}|2[0-4]\\d|25[0-5]|\\d{1,2})");
|
||||
public static final String DEFAULT_ALLOW_ALL_FLAG = "*";// 允许所有ip标志位
|
||||
public static final String DEFAULT_DENY_ALL_FLAG = "0"; // 禁止所有ip标志位
|
||||
private static final String RANGE_SPLITTER = "-"; // ip范围分隔符
|
||||
|
||||
/**
|
||||
* 根据IP白名单设置获取可用的IP列表
|
||||
*/
|
||||
public static Set<String> ipConfigList(String allowIp) {
|
||||
// 拆分出白名单正则
|
||||
Set<String> set = Sets.newHashSet(Splitter.on(CharMatcher.anyOf(",\n")).trimResults().split(allowIp));
|
||||
set = set.stream().filter(StringUtils::isNotBlank).collect(Collectors.toSet());
|
||||
return ipConfigList(Sets.newHashSet(set));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据IP白名单设置获取可用的IP列表
|
||||
*/
|
||||
public static Set<String> ipConfigList(Set<String> allowIpList) {
|
||||
Set<String> ipList = new HashSet<>(allowIpList.size());
|
||||
for (String allow : allowIpList) {
|
||||
if (allow.contains("*")) { // 处理通配符 *
|
||||
String[] ips = allow.split("\\.");
|
||||
String[] from = new String[]{"0", "0", "0", "0"};
|
||||
String[] end = new String[]{"255", "255", "255", "255"};
|
||||
List<String> tem = new ArrayList<>();
|
||||
for (int i = 0; i < ips.length; i++)
|
||||
if (ips[i].contains("*")) {
|
||||
tem = complete(ips[i]);
|
||||
from[i] = null;
|
||||
end[i] = null;
|
||||
} else {
|
||||
from[i] = ips[i];
|
||||
end[i] = ips[i];
|
||||
}
|
||||
|
||||
StringBuilder fromIP = new StringBuilder();
|
||||
StringBuilder endIP = new StringBuilder();
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (from[i] != null) {
|
||||
fromIP.append(from[i]).append(".");
|
||||
endIP.append(end[i]).append(".");
|
||||
} else {
|
||||
fromIP.append("[*].");
|
||||
endIP.append("[*].");
|
||||
}
|
||||
}
|
||||
fromIP.deleteCharAt(fromIP.length() - 1);
|
||||
endIP.deleteCharAt(endIP.length() - 1);
|
||||
|
||||
for (String s : tem) {
|
||||
String ip = fromIP.toString().replace("[*]", s.split(";")[0]) + RANGE_SPLITTER
|
||||
+ endIP.toString().replace("[*]", s.split(";")[1]);
|
||||
if (validate(ip)) {
|
||||
ipList.add(ip);
|
||||
}
|
||||
}
|
||||
} else if (allow.contains("/")) { // 处理 网段 xxx.xxx.xxx./24
|
||||
ipList.add(allow);
|
||||
} else { // 处理单个 ip 或者 范围
|
||||
if (validate(allow)) {
|
||||
ipList.add(allow);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return ipList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对单个IP节点进行范围限定
|
||||
*
|
||||
* @param arg
|
||||
* @return 返回限定后的IP范围,格式为List[10;19, 100;199]
|
||||
*/
|
||||
private static List<String> complete(String arg) {
|
||||
List<String> com = new ArrayList<>();
|
||||
int len = arg.length();
|
||||
if (len == 1) {
|
||||
com.add("0;255");
|
||||
} else if (len == 2) {
|
||||
String s1 = complete(arg, 1);
|
||||
if (s1 != null) {
|
||||
com.add(s1);
|
||||
}
|
||||
String s2 = complete(arg, 2);
|
||||
if (s2 != null) {
|
||||
com.add(s2);
|
||||
}
|
||||
} else {
|
||||
String s1 = complete(arg, 1);
|
||||
if (s1 != null) {
|
||||
com.add(s1);
|
||||
}
|
||||
}
|
||||
return com;
|
||||
}
|
||||
|
||||
private static String complete(String arg, int length) {
|
||||
String from = "";
|
||||
String end = "";
|
||||
if (length == 1) {
|
||||
from = arg.replace("*", "0");
|
||||
end = arg.replace("*", "9");
|
||||
} else {
|
||||
from = arg.replace("*", "00");
|
||||
end = arg.replace("*", "99");
|
||||
}
|
||||
if (Integer.parseInt(from) > 255) {
|
||||
return null;
|
||||
}
|
||||
if (Integer.parseInt(end) > 255) {
|
||||
end = "255";
|
||||
}
|
||||
return from + ";" + end;
|
||||
}
|
||||
|
||||
/**
|
||||
* 在添加至白名单时进行格式校验
|
||||
*/
|
||||
private static boolean validate(String ip) {
|
||||
String[] temp = ip.split(RANGE_SPLITTER);
|
||||
for (String s : temp) {
|
||||
if (!IP_PATTERN.matcher(s).matches()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据IP,及可用Ip列表来判断ip是否包含在白名单之中
|
||||
*/
|
||||
public static boolean match(String ip, Set<String> ipList) {
|
||||
if (CollectionUtils.isEmpty(ipList)) {
|
||||
return false;
|
||||
}
|
||||
if (ipList.contains(ip)) {
|
||||
return true;
|
||||
}
|
||||
IPAddress ipAddress = null;
|
||||
try {
|
||||
ipAddress = new IPAddressString(ip).toAddress();
|
||||
} catch (AddressStringException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
for (String allow : ipList) {
|
||||
if (allow.contains(RANGE_SPLITTER)) { // 处理 类似 192.168.0.0-192.168.2.1
|
||||
String[] tempAllow = allow.split(RANGE_SPLITTER);
|
||||
String[] from = tempAllow[0].split("\\.");
|
||||
String[] end = tempAllow[1].split("\\.");
|
||||
String[] tag = ip.split("\\.");
|
||||
boolean check = true;
|
||||
for (int i = 0; i < 4; i++) { // 对IP从左到右进行逐段匹配
|
||||
int s = Integer.parseInt(from[i]);
|
||||
int t = Integer.parseInt(tag[i]);
|
||||
int e = Integer.parseInt(end[i]);
|
||||
if (!(s <= t && t <= e)) {
|
||||
check = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (check) {
|
||||
return true;
|
||||
}
|
||||
} else if (allow.contains("/")) { // 处理 网段 xxx.xxx.xxx./24
|
||||
int splitIndex = allow.indexOf("/");
|
||||
// 取出子网段
|
||||
String ipSegment = allow.substring(0, splitIndex); // 192.168.3.0
|
||||
// 子网数
|
||||
String netmask = allow.substring(splitIndex + 1); // 24
|
||||
// ip 转二进制
|
||||
long ipLong = ipToLong(ip);
|
||||
// 子网二进制
|
||||
long maskLong = (2L << 32 - 1) - (2L << (32 - Integer.parseInt(netmask)) - 1);
|
||||
// ip与和子网相与 得到 网络地址
|
||||
String calcSegment = longToIP(ipLong & maskLong);
|
||||
// 如果计算得出网络地址和库中网络地址相同 则合法
|
||||
if (ipSegment.equals(calcSegment)) {
|
||||
return true;
|
||||
}
|
||||
} else if (allow.contains("*")) {
|
||||
IPAddress rangeAddress = new IPAddressString(allow).getAddress();
|
||||
if (rangeAddress.contains(ipAddress)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据IP地址,及IP白名单设置规则判断IP是否包含在白名单
|
||||
*/
|
||||
public static boolean match(String ip, String ipWhiteConfig) {
|
||||
if (null == ip || "".equals(ip)) {
|
||||
return false;
|
||||
}
|
||||
// ip格式不对
|
||||
if (!IP_PATTERN.matcher(ip).matches()) {
|
||||
return false;
|
||||
}
|
||||
if (DEFAULT_ALLOW_ALL_FLAG.equals(ipWhiteConfig)) {
|
||||
return true;
|
||||
}
|
||||
if (DEFAULT_DENY_ALL_FLAG.equals(ipWhiteConfig)) {
|
||||
return false;
|
||||
}
|
||||
Set<String> ipList = ipConfigList(ipWhiteConfig);
|
||||
return match(ip, ipList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 127.0.0.1形式的IP地址 转换成 10进制整数形式
|
||||
*/
|
||||
private static long ipToLong(String strIP) {
|
||||
long[] ip = new long[4];
|
||||
// 先找到IP地址字符串中.的位置
|
||||
int position1 = strIP.indexOf(".");
|
||||
int position2 = strIP.indexOf(".", position1 + 1);
|
||||
int position3 = strIP.indexOf(".", position2 + 1);
|
||||
// 将每个.之间的字符串转换成整型
|
||||
ip[0] = Long.parseLong(strIP.substring(0, position1));
|
||||
ip[1] = Long.parseLong(strIP.substring(position1 + 1, position2));
|
||||
ip[2] = Long.parseLong(strIP.substring(position2 + 1, position3));
|
||||
ip[3] = Long.parseLong(strIP.substring(position3 + 1));
|
||||
return (ip[0] << 24) + (ip[1] << 16) + (ip[2] << 8) + ip[3];
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 10进制整数形式 转换成 127.0.0.1形式的IP地址
|
||||
*/
|
||||
private static String longToIP(long longIP) {
|
||||
// 直接右移24位
|
||||
return "" + (longIP >>> 24) +
|
||||
"." +
|
||||
// 将高8位置0,然后右移16位
|
||||
((longIP & 0x00FFFFFF) >>> 16) +
|
||||
"." +
|
||||
((longIP & 0x0000FFFF) >>> 8) +
|
||||
"." +
|
||||
(longIP & 0x000000FF);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
package com.fizzgate.plugin.ip.util;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
|
||||
import java.net.*;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* @author huahua
|
||||
*/
|
||||
public class IpUtils {
|
||||
|
||||
/*
|
||||
* 取所有IP段的私有IP段
|
||||
* A类 私有地址 10.0.0.0---10.255.255.255 保留地址 127.0.0.0---127.255.255.255
|
||||
* B类 私有地址 172.16.0.0-172.31.255.255
|
||||
* C类 私有地址 192.168.0.0-192.168.255.255
|
||||
* D类 地址不分网络地址和主机地址
|
||||
* E类 地址不分网络地址和主机地址
|
||||
*/
|
||||
private static long
|
||||
aBegin = ipToLong("10.0.0.0"),
|
||||
aEnd = ipToLong("10.255.255.255"),
|
||||
bBegin = ipToLong("172.16.0.0"),
|
||||
bEnd = ipToLong("172.31.255.255"),
|
||||
cBegin = ipToLong("192.168.0.0"),
|
||||
cEnd = ipToLong("192.168.255.255"),
|
||||
saveBegin = ipToLong("127.0.0.0"),
|
||||
saveEnd = ipToLong("127.255.255.255");
|
||||
|
||||
// 跟IP有关需要做判断的header参数
|
||||
private static String
|
||||
CLIENT_IP = "clientip",
|
||||
X_FORWARDED_FOR = "x-forwarded-for",
|
||||
PROXY_CLIENT_IP = "proxy-client-ip",
|
||||
WL_PROXY_CLIENT_IP = "wl-proxy-client-ip";
|
||||
private static Set<String> ipHeaderNames = Sets.newHashSet(CLIENT_IP, X_FORWARDED_FOR, PROXY_CLIENT_IP, WL_PROXY_CLIENT_IP);
|
||||
|
||||
public static String getServerHttpRequestIp(ServerHttpRequest request) throws SocketException {
|
||||
// 防止在header中的参数名有大小写之分,重新将需要处理的参数值和内容装填入Map中
|
||||
Map<String, List<String>> ipHeaders = Maps.newHashMap();
|
||||
Set<Map.Entry<String, List<String>>> headers = request.getHeaders().entrySet();
|
||||
for (Map.Entry<String, List<String>> header : headers) {
|
||||
String name = header.getKey();
|
||||
String lowerCaseName = name.toLowerCase();
|
||||
if (ipHeaderNames.contains(lowerCaseName)) {
|
||||
List<String> values = Lists.newArrayList();
|
||||
for (String headerValue : header.getValue()) {
|
||||
if (StringUtils.indexOf(headerValue, ",") >= 0) {
|
||||
String[] headerValueArr = StringUtils.split(headerValue, ",");
|
||||
if (headerValueArr.length > 0) {
|
||||
for (String s : headerValueArr) {
|
||||
values.add(StringUtils.trimToEmpty(s));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
values.add(headerValue);
|
||||
}
|
||||
}
|
||||
ipHeaders.put(lowerCaseName, values); // 装填key和value
|
||||
}
|
||||
}
|
||||
//取正确的IP
|
||||
String ipAddress = null;
|
||||
// 取clientip
|
||||
List<String> clientIpList = ipHeaders.get(CLIENT_IP); // 取clientip与client-ip有区别
|
||||
ipAddress = fetchPublicIp(clientIpList);
|
||||
// 若clientip为空或者是内网IP则取x-forwarded-for
|
||||
if (StringUtils.isBlank(ipAddress)) {
|
||||
List<String> xForwardedIpList = ipHeaders.get(X_FORWARDED_FOR);
|
||||
ipAddress = fetchPublicIp(xForwardedIpList);
|
||||
}
|
||||
// 若x-forwarded-for为空则取proxy-client-ip
|
||||
if (StringUtils.isBlank(ipAddress)) {
|
||||
List<String> proxyClientIpList = ipHeaders.get(PROXY_CLIENT_IP);
|
||||
ipAddress = fetchPublicIp(proxyClientIpList);
|
||||
}
|
||||
// 若proxy-client-ip为空则取wl-proxy-client-ip
|
||||
if (StringUtils.isBlank(ipAddress)) {
|
||||
List<String> wlProxyClientIpList = ipHeaders.get(WL_PROXY_CLIENT_IP);
|
||||
ipAddress = fetchPublicIp(wlProxyClientIpList);
|
||||
}
|
||||
// 若wl-proxy-client-ip为空则取RemoteAddr
|
||||
if (StringUtils.isBlank(ipAddress)) {
|
||||
InetSocketAddress inetSocketAddress = request.getRemoteAddress();
|
||||
if (inetSocketAddress != null) {
|
||||
InetAddress inetAddress = inetSocketAddress.getAddress();
|
||||
if (inetAddress instanceof Inet4Address) {
|
||||
ipAddress = inetAddress.getHostAddress();
|
||||
}
|
||||
}
|
||||
if (StringUtils.equals(ipAddress, "127.0.0.1")) {
|
||||
// 根据网卡取本机配置的IP
|
||||
ipAddress = getLocalIp();
|
||||
}
|
||||
}
|
||||
return ipAddress;
|
||||
}
|
||||
|
||||
public static String getLocalIp() throws SocketException {
|
||||
Enumeration allNetInterfaces = NetworkInterface.getNetworkInterfaces();
|
||||
InetAddress ip;
|
||||
while (allNetInterfaces.hasMoreElements()) {
|
||||
NetworkInterface netInterface = (NetworkInterface) allNetInterfaces.nextElement();
|
||||
Enumeration addresses = netInterface.getInetAddresses();
|
||||
while (addresses.hasMoreElements()) {
|
||||
ip = (InetAddress) addresses.nextElement();
|
||||
if (ip instanceof Inet4Address) {
|
||||
return ip.getHostAddress();
|
||||
}
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public static long localIpNumber() throws SocketException {
|
||||
return ipToLong(getLocalIp());
|
||||
}
|
||||
|
||||
private static long ipToLong(String ipAddress) {
|
||||
long result = 0L;
|
||||
String[] ipAddressInArray = ipAddress.split("\\.");
|
||||
if (ipAddressInArray.length != 4) {
|
||||
return 0;
|
||||
}
|
||||
for (int i = 3; i >= 0; i--) {
|
||||
long ip = 0;
|
||||
try {
|
||||
ip = Long.parseLong(ipAddressInArray[3 - i]);
|
||||
} catch (NumberFormatException e) {
|
||||
// ignore
|
||||
}
|
||||
result |= ip << (i * 8);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String fetchPublicIp(List<String> ipAddressList) {
|
||||
String ipAddress = null;
|
||||
if (ipAddressList == null || ipAddressList.size() <= 0) {
|
||||
return ipAddress;
|
||||
}
|
||||
for (String ip : ipAddressList) {
|
||||
long ipNum = ipToLong(ip);
|
||||
if (isIpAddress(ip)
|
||||
&& !isInner(ipNum, aBegin, aEnd)
|
||||
&& !isInner(ipNum, bBegin, bEnd)
|
||||
&& !isInner(ipNum, cBegin, cEnd)
|
||||
&& !isInner(ipNum, saveBegin, saveEnd)) {
|
||||
ipAddress = ip;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ipAddress;
|
||||
}
|
||||
|
||||
public static boolean isInnerIP(String ipAddress) {
|
||||
long ipNum = ipToLong(ipAddress);
|
||||
return isInner(ipNum, aBegin, aEnd) || isInner(ipNum, bBegin, bEnd) || isInner(ipNum, cBegin, cEnd) || isInner(ipNum, saveBegin, saveEnd);
|
||||
}
|
||||
|
||||
private static boolean isInner(long userIp, long begin, long end) {
|
||||
return (userIp >= begin) && (userIp <= end);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检验是否是合法的IP地址
|
||||
*
|
||||
* @param address String IP地址
|
||||
* @return boolean IP地址是否合法
|
||||
*/
|
||||
public static boolean isIpAddress(String address) {
|
||||
if (StringUtils.isEmpty(address)) {
|
||||
return false;
|
||||
}
|
||||
String regex = "((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})(\\.((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})){3}";
|
||||
Pattern p = Pattern.compile(regex);
|
||||
Matcher m = p.matcher(address);
|
||||
return m.matches();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>fizz-gateway-community</artifactId>
|
||||
<groupId>com.fizzgate</groupId>
|
||||
<version>2.7.1</version>
|
||||
<version>2.7.2</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
40
pom.xml
40
pom.xml
@@ -6,11 +6,11 @@
|
||||
<properties>
|
||||
<!--<java.version>1.8</java.version>-->
|
||||
<spring-boot.version>2.2.13.RELEASE</spring-boot.version>
|
||||
<spring-framework.version>5.2.22.RELEASE</spring-framework.version>
|
||||
<spring-framework.version>5.2.23.RELEASE</spring-framework.version>
|
||||
<reactor-bom.version>Dysprosium-SR25</reactor-bom.version>
|
||||
<lettuce.version>5.3.7.RELEASE</lettuce.version>
|
||||
<nacos.cloud.version>2.2.7.RELEASE</nacos.cloud.version>
|
||||
<netty.version>4.1.89.Final</netty.version>
|
||||
<netty.version>4.1.91.Final</netty.version>
|
||||
<httpcore.version>4.4.16</httpcore.version>
|
||||
<log4j2.version>2.17.2</log4j2.version>
|
||||
<slf4j.version>1.7.36</slf4j.version>
|
||||
@@ -38,7 +38,7 @@
|
||||
<artifactId>fizz-gateway-community</artifactId>
|
||||
<name>${project.artifactId}</name>
|
||||
<description>fizz gateway community</description>
|
||||
<version>2.7.1</version>
|
||||
<version>2.7.2</version>
|
||||
<packaging>pom</packaging>
|
||||
<modules>
|
||||
<module>fizz-common</module>
|
||||
@@ -418,7 +418,7 @@
|
||||
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-tcnative</artifactId>
|
||||
<artifactId>netty-tcnative-classes</artifactId>
|
||||
<version>${netty-tcnative.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -428,7 +428,37 @@
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-tcnative-classes</artifactId>
|
||||
<artifactId>netty-tcnative-boringssl-static</artifactId>
|
||||
<version>${netty-tcnative.version}</version>
|
||||
<classifier>linux-x86_64</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-tcnative-boringssl-static</artifactId>
|
||||
<version>${netty-tcnative.version}</version>
|
||||
<classifier>linux-aarch_64</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-tcnative-boringssl-static</artifactId>
|
||||
<version>${netty-tcnative.version}</version>
|
||||
<classifier>osx-x86_64</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-tcnative-boringssl-static</artifactId>
|
||||
<version>${netty-tcnative.version}</version>
|
||||
<classifier>osx-aarch_64</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-tcnative-boringssl-static</artifactId>
|
||||
<version>${netty-tcnative.version}</version>
|
||||
<classifier>windows-x86_64</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-tcnative</artifactId>
|
||||
<version>${netty-tcnative.version}</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user