diff --git a/fizz-core/src/main/java/we/api/pairing/FizzApiPairingController.java b/fizz-core/src/main/java/we/api/pairing/FizzApiPairingController.java
new file mode 100644
index 0000000..a67630c
--- /dev/null
+++ b/fizz-core/src/main/java/we/api/pairing/FizzApiPairingController.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2020 the original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package we.api.pairing;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.http.server.reactive.ServerHttpResponse;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Mono;
+import we.config.SystemConfig;
+import we.plugin.auth.App;
+import we.plugin.auth.AppService;
+import we.util.DateTimeUtils;
+import we.util.WebUtils;
+
+import javax.annotation.Resource;
+import java.time.LocalDateTime;
+import java.util.List;
+
+/**
+ * @author hongqiaowei
+ */
+
+@RestController
+@RequestMapping(SystemConfig.DEFAULT_GATEWAY_PREFIX + "/_fizz-pairing")
+public class FizzApiPairingController {
+
+ private static final Logger log = LoggerFactory.getLogger(FizzApiPairingController.class);
+
+ @Resource
+ private SystemConfig systemConfig;
+
+ @Resource
+ private AppService appService;
+
+ @Value("${fizz.api.pairing.request.timeliness:300}")
+ private int timeliness = 300; // unit: sec
+
+ @GetMapping("/pair")
+ public Mono pair(ServerWebExchange exchange) {
+
+ ServerHttpRequest request = exchange.getRequest();
+ ServerHttpResponse response = exchange.getResponse();
+ response.setStatusCode(HttpStatus.FORBIDDEN);
+ response.getHeaders().setContentType(MediaType.TEXT_PLAIN);
+ HttpHeaders headers = request.getHeaders();
+
+ String appId = WebUtils.getAppId(exchange);
+ if (appId == null) {
+ return WebUtils.buildDirectResponse(response, null, null, "请求无应用信息");
+ }
+ App app = appService.getApp(appId);
+ if (app == null) {
+ return WebUtils.buildDirectResponse(response, null, null, "系统无" + appId + "应用信息");
+ }
+
+ String timestamp = getTimestamp(headers);
+ if (timestamp == null) {
+ return WebUtils.buildDirectResponse(response, null, null, "请求无时间戳");
+ }
+ try {
+ long ts = Long.parseLong(timestamp);
+ LocalDateTime now = LocalDateTime.now();
+ long start = DateTimeUtils.toMillis(now.minusSeconds(timeliness));
+ long end = DateTimeUtils.toMillis(now.plusSeconds (timeliness));
+ if (start <= ts && ts <= end) {
+ // valid
+ } else {
+ return WebUtils.buildDirectResponse(response, null, null, "请求时间戳无效");
+ }
+ } catch (NumberFormatException e) {
+ return WebUtils.buildDirectResponse(response, null, null, "请求时间戳无效");
+ }
+
+ String sign = getSign(headers);
+ if (sign == null) {
+ return WebUtils.buildDirectResponse(response, null, null, "请求未签名");
+ }
+
+ boolean equals = PairingUtils.checkSign(appId, timestamp, app.secretkey, sign);
+
+ if (equals) {
+ // TODO: 响应文档集
+ return Mono.empty();
+ } else {
+ log.warn("request authority: app {}, timestamp {}, sign {} invalid", appId, timestamp, sign);
+ return WebUtils.buildDirectResponse(response, null, null, "请求签名无效");
+ }
+ }
+
+ private String getSign(HttpHeaders headers) {
+ List signHdrs = systemConfig.getSignHeaders();
+ for (int i = 0; i < signHdrs.size(); i++) {
+ String v = headers.getFirst(signHdrs.get(i));
+ if (v != null) {
+ return v;
+ }
+ }
+ return null;
+ }
+
+ private String getTimestamp(HttpHeaders headers) {
+ List tsHdrs = systemConfig.getTimestampHeaders();
+ for (int i = 0; i < tsHdrs.size(); i++) {
+ String v = headers.getFirst(tsHdrs.get(i));
+ if (v != null) {
+ return v;
+ }
+ }
+ return null;
+ }
+}
diff --git a/fizz-core/src/main/java/we/api/pairing/PairingUtils.java b/fizz-core/src/main/java/we/api/pairing/PairingUtils.java
new file mode 100644
index 0000000..190636d
--- /dev/null
+++ b/fizz-core/src/main/java/we/api/pairing/PairingUtils.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2020 the original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package we.api.pairing;
+
+import we.util.Consts;
+import we.util.ThreadContext;
+
+/**
+ * @author hongqiaowei
+ */
+
+public abstract class PairingUtils extends org.apache.commons.codec.digest.DigestUtils {
+
+ private PairingUtils() {
+ }
+
+ public static String sign(String app, String timestamp, String secretKey) {
+ StringBuilder b = ThreadContext.getStringBuilder(ThreadContext.sb0);
+ b.append(app) .append(Consts.S.UNDER_LINE)
+ .append(timestamp).append(Consts.S.UNDER_LINE)
+ .append(secretKey);
+ return sha256Hex(b.toString());
+ }
+
+ public static String sign(String app, long timestamp, String secretKey) {
+ return sign(app, String.valueOf(timestamp), secretKey);
+ }
+
+ public static boolean checkSign(String app, String timestamp, String secretKey, String sign) {
+ String s = sign(app, timestamp, secretKey);
+ return s.equals(sign);
+ }
+
+ public static boolean checkSign(String app, long timestamp, String secretKey, String sign) {
+ return checkSign(app, String.valueOf(timestamp), secretKey, sign);
+ }
+}
diff --git a/fizz-core/src/main/java/we/filter/FizzWebFilter.java b/fizz-core/src/main/java/we/filter/FizzWebFilter.java
index 428ced3..664fd68 100644
--- a/fizz-core/src/main/java/we/filter/FizzWebFilter.java
+++ b/fizz-core/src/main/java/we/filter/FizzWebFilter.java
@@ -21,6 +21,7 @@ import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
+import we.util.WebUtils;
/**
* @author hongqiaowei
@@ -28,15 +29,12 @@ import reactor.core.publisher.Mono;
public abstract class FizzWebFilter implements WebFilter {
- private static final String admin = "admin";
- private static final String actuator = "actuator";
-
@Override
public Mono filter(ServerWebExchange exchange, WebFilterChain chain) {
- if (exchange.getAttribute(FlowControlFilter.ADMIN_REQUEST) == null) {
- return doFilter(exchange, chain);
- } else {
+ if (WebUtils.isAdminReq(exchange) || WebUtils.isFizzApiReq(exchange)) {
return chain.filter(exchange);
+ } else {
+ return doFilter(exchange, chain);
}
}
diff --git a/fizz-core/src/main/java/we/filter/FlowControlFilter.java b/fizz-core/src/main/java/we/filter/FlowControlFilter.java
index 679725c..5f7a00e 100644
--- a/fizz-core/src/main/java/we/filter/FlowControlFilter.java
+++ b/fizz-core/src/main/java/we/filter/FlowControlFilter.java
@@ -67,7 +67,8 @@ public class FlowControlFilter extends FizzWebFilter {
private static final String defaultFizzTraceIdValueStrategy = "requestId";
- public static final String ADMIN_REQUEST = "$a";
+ private static final String _fizz = "_fizz-";
+
@Resource
private FlowControlFilterProperties flowControlFilterProperties;
@@ -96,17 +97,21 @@ public class FlowControlFilter extends FizzWebFilter {
return WebUtils.responseError(exchange, HttpStatus.INTERNAL_SERVER_ERROR.value(), "request path should like /optional-prefix/service-name/real-biz-path");
}
String service = path.substring(1, secFS);
- boolean adminReq = false, proxyTestReq = false;
+ boolean adminReq = false, proxyTestReq = false, fizzApiReq = false;
if (service.equals(admin) || service.equals(actuator)) {
adminReq = true;
- exchange.getAttributes().put(ADMIN_REQUEST, Consts.S.EMPTY);
+ exchange.getAttributes().put(WebUtils.ADMIN_REQUEST, Consts.S.EMPTY);
} else if (service.equals(SystemConfig.DEFAULT_GATEWAY_TEST)) {
proxyTestReq = true;
} else {
service = WebUtils.getClientService(exchange);
+ if (service.startsWith(_fizz)) {
+ fizzApiReq = true;
+ exchange.getAttributes().put(WebUtils.FIZZ_API_REQUEST, Consts.S.EMPTY);
+ }
}
- if (flowControlFilterProperties.isFlowControl() && !adminReq && !proxyTestReq) {
+ if (flowControlFilterProperties.isFlowControl() && !adminReq && !proxyTestReq && !fizzApiReq) {
String traceId = WebUtils.getTraceId(exchange);
LogService.setBizId(traceId);
if (!apiConfigService.serviceConfigMap.containsKey(service)) {
diff --git a/fizz-core/src/main/java/we/plugin/auth/ApiConfigService.java b/fizz-core/src/main/java/we/plugin/auth/ApiConfigService.java
index 35f68e1..6a78b32 100644
--- a/fizz-core/src/main/java/we/plugin/auth/ApiConfigService.java
+++ b/fizz-core/src/main/java/we/plugin/auth/ApiConfigService.java
@@ -529,9 +529,9 @@ public class ApiConfigService {
private String getTimestamp(HttpHeaders reqHdrs) {
List tsHdrs = systemConfig.getTimestampHeaders();
for (int i = 0; i < tsHdrs.size(); i++) {
- String a = reqHdrs.getFirst(tsHdrs.get(i));
- if (a != null) {
- return a;
+ String v = reqHdrs.getFirst(tsHdrs.get(i));
+ if (v != null) {
+ return v;
}
}
return null;
@@ -540,9 +540,9 @@ public class ApiConfigService {
private String getSign(HttpHeaders reqHdrs) {
List signHdrs = systemConfig.getSignHeaders();
for (int i = 0; i < signHdrs.size(); i++) {
- String a = reqHdrs.getFirst(signHdrs.get(i));
- if (a != null) {
- return a;
+ String v = reqHdrs.getFirst(signHdrs.get(i));
+ if (v != null) {
+ return v;
}
}
return null;
diff --git a/fizz-core/src/main/java/we/util/WebUtils.java b/fizz-core/src/main/java/we/util/WebUtils.java
index e8e9450..ebeb159 100644
--- a/fizz-core/src/main/java/we/util/WebUtils.java
+++ b/fizz-core/src/main/java/we/util/WebUtils.java
@@ -106,10 +106,22 @@ public abstract class WebUtils {
public static Set LOG_HEADER_SET = Collections.emptySet();
+ public static final String ADMIN_REQUEST = "ar@";
+
+ public static final String FIZZ_API_REQUEST = "far@";
+
private WebUtils() {
}
+ public static boolean isAdminReq(ServerWebExchange exchange) {
+ return exchange.getAttribute(ADMIN_REQUEST) != null;
+ }
+
+ public static boolean isFizzApiReq(ServerWebExchange exchange) {
+ return exchange.getAttribute(FIZZ_API_REQUEST) != null;
+ }
+
public static void setGatewayPrefix(String p) {
gatewayPrefix = p;
}