diff --git a/pom.xml b/pom.xml
index 8fc6ead..8ceb4f0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,107 +1,134 @@
- 4.0.0
-
- org.springframework.boot
- spring-boot-starter-parent
- 2.7.9
-
-
- com.longfor
- bff_netflix
- 0.0.1-SNAPSHOT
- bff_netflix
- bff_netflix
-
- 1.8
- 5.1.17
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.7.9
+
+
+ com.longfor
+ bff_netflix
+ 0.0.1-SNAPSHOT
+ bff_netflix
+ bff_netflix
+
+ 1.8
+ 5.1.17
+ 2021.0.5
+
+
+
+
+ com.netflix.graphql.dgs
+ graphql-dgs-platform-dependencies
+
+ 4.9.16
+ pom
+ import
+
+
+ org.springframework.cloud
+ spring-cloud-dependencies
+ ${spring-cloud.version}
+ pom
+ import
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ com.netflix.graphql.dgs
+ graphql-dgs-spring-boot-starter
+
+
+ com.netflix.graphql.dgs
+ graphql-dgs-pagination
+
+
+ com.netflix.graphql.dgs.codegen
+ graphql-dgs-codegen-client-core
+ ${dgs.codegen.version}
+
+
+ org.projectlombok
+ lombok
+ true
+
+
+ org.apache.commons
+ commons-lang3
+ 3.7
+
+
+ com.alibaba
+ fastjson
+ 1.2.78
+
+
+ cn.hutool
+ hutool-all
+ 5.4.0
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
-
-
-
-
- com.netflix.graphql.dgs
- graphql-dgs-platform-dependencies
-
- 4.9.16
- pom
- import
-
-
-
-
-
- org.springframework.boot
- spring-boot-starter
-
-
- com.netflix.graphql.dgs
- graphql-dgs-spring-boot-starter
-
-
- com.netflix.graphql.dgs
- graphql-dgs-pagination
-
-
- com.netflix.graphql.dgs.codegen
- graphql-dgs-codegen-client-core
- ${dgs.codegen.version}
-
-
- org.projectlombok
- lombok
- true
-
-
- org.springframework.boot
- spring-boot-starter-test
- test
-
-
+
-
-
-
- org.springframework.boot
- spring-boot-maven-plugin
-
-
-
- org.projectlombok
- lombok
-
-
-
-
-
- io.github.deweyjose
- graphqlcodegen-maven-plugin
- 1.30
-
-
-
- generate
-
-
-
-
- true
- true
- com.longfor
-
- src/main/resources/schema
-
- true
- true
-
-
- ]]>
-
-
-
-
-
-
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+ org.projectlombok
+ lombok
+
+
+
+
+
+ io.github.deweyjose
+ graphqlcodegen-maven-plugin
+ 1.30
+
+
+
+ generate
+
+
+
+
+ true
+ true
+ com.longfor
+
+ src/main/resources/schema
+
+ true
+ true
+
+
+ ]]>
+
+
+
+
+
+
diff --git a/src/main/java/com/longfor/bff_netflix/autoconfigure/ApiSignAutoConfigure.java b/src/main/java/com/longfor/bff_netflix/autoconfigure/ApiSignAutoConfigure.java
new file mode 100644
index 0000000..33294d7
--- /dev/null
+++ b/src/main/java/com/longfor/bff_netflix/autoconfigure/ApiSignAutoConfigure.java
@@ -0,0 +1,48 @@
+package com.longfor.bff_netflix.autoconfigure;
+
+import com.longfor.bff_netflix.autoconfigure.ApiSignConfigLoad;
+import com.longfor.bff_netflix.entity.AppSecretInfo;
+import com.longfor.bff_netflix.interceptor.RequestInterceptor;
+import com.longfor.bff_netflix.services.SignService;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+
+import javax.annotation.Resource;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Configuration
+@ConditionalOnWebApplication
+@ComponentScan(value = {"com.longfor.bff_netflix.*"})
+public class ApiSignAutoConfigure {
+
+ @Resource
+ private List signServices;
+
+ @Resource
+ private ApiSignConfigLoad apiSignConfigLoad;
+
+ @Bean
+ public RequestInterceptor requestInterceptor(){
+ final Map signServiceMap = new HashMap<>();
+ final Map appSecretInfoMap = new HashMap<>();
+
+ for (SignService signService : signServices) {
+ if (StringUtils.isBlank(signService.signMethod())){
+ throw new IllegalArgumentException("验签方式配置错误");
+ }
+ signServiceMap.put(signService.signMethod(), signService);
+ }
+ for (AppSecretInfo appSecretInfo : apiSignConfigLoad.loadAppSecretInfos()) {
+
+ }
+
+ apiSignConfigLoad.loadAppSecretInfos().forEach(a -> appSecretInfoMap.put(a.getAppKey(), a));
+ return new RequestInterceptor(signServiceMap, appSecretInfoMap, apiSignConfigLoad);
+ }
+
+}
diff --git a/src/main/java/com/longfor/bff_netflix/autoconfigure/ApiSignConfigLoad.java b/src/main/java/com/longfor/bff_netflix/autoconfigure/ApiSignConfigLoad.java
new file mode 100644
index 0000000..fe62e1d
--- /dev/null
+++ b/src/main/java/com/longfor/bff_netflix/autoconfigure/ApiSignConfigLoad.java
@@ -0,0 +1,41 @@
+package com.longfor.bff_netflix.autoconfigure;
+
+import com.alibaba.fastjson.JSONObject;
+import com.longfor.bff_netflix.entity.AppSecretInfo;
+import org.springframework.context.annotation.Configuration;
+
+import javax.annotation.Resource;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+@Configuration
+public class ApiSignConfigLoad {
+
+ /**
+ * 加载调用方应用签名信息
+ */
+ public List loadAppSecretInfos() {
+ AppSecretInfo sample = new AppSecretInfo();
+ sample.setAppKey("123");
+ sample.setAppSecret("kkk");
+ return Arrays.asList(sample);
+ } //TODO: Load the config from Apollo Server
+
+ /**
+ * 是否验签,默认所有接口都参与验签
+ */
+ public boolean isSignatureValidationRequired(String path) {
+ return false;
+ }
+
+ /**
+ * 构建业务自定义错误响应,系统提供默认异常实现
+ */
+ public Object buildErrorResult(String message) {
+ JSONObject result = new JSONObject();
+ result.put("code", -1);
+ result.put("msg", message);
+ return result;
+ }
+}
diff --git a/src/main/java/com/longfor/bff_netflix/autoconfigure/CachedHttpServletRequestWrapper.java b/src/main/java/com/longfor/bff_netflix/autoconfigure/CachedHttpServletRequestWrapper.java
new file mode 100644
index 0000000..38c1bb0
--- /dev/null
+++ b/src/main/java/com/longfor/bff_netflix/autoconfigure/CachedHttpServletRequestWrapper.java
@@ -0,0 +1,130 @@
+package com.longfor.bff_netflix.autoconfigure;
+
+import javax.servlet.ReadListener;
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import java.io.*;
+import javax.servlet.ServletRequest;
+
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+import java.nio.charset.Charset;
+
+/**
+ * @author rx6
+ */
+public class CachedHttpServletRequestWrapper extends HttpServletRequestWrapper {
+ private final byte[] cachedContent;
+ private static Logger log = LoggerFactory.getLogger(CachedHttpServletRequestWrapper.class);
+
+ private String encoding;
+
+ public CachedHttpServletRequestWrapper(HttpServletRequest request, String encoding) throws IOException {
+ super(request);
+ this.encoding = StringUtils.isEmpty(encoding) ? "UTF-8" : encoding;
+ cachedContent = writeToCachedContent(request.getInputStream());
+ }
+
+ /**
+ * 获取请求Body
+ *
+ * @return
+ */
+ public String getBodyString() {
+ StringBuilder sb = new StringBuilder();
+ InputStream inputStream = null;
+ BufferedReader reader = null;
+ try {
+ inputStream = new ByteArrayInputStream(cachedContent);
+ reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName(this.encoding)));
+ String line = "";
+ while ((line = reader.readLine()) != null) {
+ sb.append(line);
+ }
+ } catch (IOException e) {
+ log.error("出错了", e);
+ } finally {
+ if (inputStream != null) {
+ try {
+ inputStream.close();
+ } catch (IOException e) {
+ log.error("inputStream的io出错了", e);
+ }
+ }
+ if (reader != null) {
+ try {
+ reader.close();
+ } catch (IOException e) {
+ log.error("读流关闭io出错了", e);
+ }
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Description: 复制输入流
+ *
+ * @param inputStream
+ * @return
+ */
+ public byte[] writeToCachedContent(ServletInputStream inputStream) throws IOException {
+ try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
+ byte[] buffer = new byte[1024];
+ int len;
+ try {
+ while ((len = inputStream.read(buffer)) > -1) {
+ byteArrayOutputStream.write(buffer, 0, len);
+ }
+ byteArrayOutputStream.flush();
+ } catch (IOException e) {
+ log.error("出错了", e);
+ }
+ byte[] bytes = byteArrayOutputStream.toByteArray();
+ return bytes;
+ }
+ }
+
+ @Override
+ public BufferedReader getReader() throws IOException {
+ return new BufferedReader(new InputStreamReader(this.getInputStream()));
+ }
+
+ @Override
+ public ServletInputStream getInputStream() throws IOException {
+ return new ContentCachingInputStream(this.cachedContent);
+ }
+
+ private class ContentCachingInputStream extends ServletInputStream {
+
+ private ByteArrayInputStream inputStream;
+
+ public ContentCachingInputStream(byte[] bytes) {
+ this.inputStream = new ByteArrayInputStream(bytes);
+ }
+
+ @Override
+ public boolean isFinished() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isReady() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setReadListener(ReadListener readListener) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int read() throws IOException {
+ return inputStream.read();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/longfor/bff_netflix/autoconfigure/CachedServletRequestWrapper.java b/src/main/java/com/longfor/bff_netflix/autoconfigure/CachedServletRequestWrapper.java
new file mode 100644
index 0000000..452483d
--- /dev/null
+++ b/src/main/java/com/longfor/bff_netflix/autoconfigure/CachedServletRequestWrapper.java
@@ -0,0 +1,127 @@
+package com.longfor.bff_netflix.autoconfigure;
+
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.ReadListener;
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import java.io.*;
+import java.nio.charset.Charset;
+
+/**
+ * @author rxy
+ */
+public class CachedServletRequestWrapper extends HttpServletRequestWrapper {
+ private final byte[] cachedContent;
+ private static Logger log = LoggerFactory.getLogger(CachedServletRequestWrapper.class);
+
+ private String encoding;
+
+ public CachedServletRequestWrapper(HttpServletRequest request, String encoding) throws IOException {
+ super(request);
+ this.encoding = StringUtils.isEmpty(encoding) ? "UTF-8" : encoding;
+ cachedContent = writeToCachedContent(request.getInputStream());
+ }
+
+ /**
+ * 获取请求Body
+ *
+ * @return
+ */
+ public String getBodyString() {
+ StringBuilder sb = new StringBuilder();
+ InputStream inputStream = null;
+ BufferedReader reader = null;
+ try {
+ inputStream = new ByteArrayInputStream(cachedContent);
+ reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName(this.encoding)));
+ String line = "";
+ while ((line = reader.readLine()) != null) {
+ sb.append(line);
+ }
+ } catch (IOException e) {
+ log.error("出错了", e);
+ } finally {
+ if (inputStream != null) {
+ try {
+ inputStream.close();
+ } catch (IOException e) {
+ log.error("inputStream的io出错了", e);
+ }
+ }
+ if (reader != null) {
+ try {
+ reader.close();
+ } catch (IOException e) {
+ log.error("读流关闭io出错了", e);
+ }
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Description: 复制输入流
+ *
+ * @param inputStream
+ * @return
+ */
+ public byte[] writeToCachedContent(ServletInputStream inputStream) throws IOException {
+ try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
+ byte[] buffer = new byte[1024];
+ int len;
+ try {
+ while ((len = inputStream.read(buffer)) > -1) {
+ byteArrayOutputStream.write(buffer, 0, len);
+ }
+ byteArrayOutputStream.flush();
+ } catch (IOException e) {
+ log.error("出错了", e);
+ }
+ byte[] bytes = byteArrayOutputStream.toByteArray();
+ return bytes;
+ }
+ }
+
+ @Override
+ public BufferedReader getReader() throws IOException {
+ return new BufferedReader(new InputStreamReader(this.getInputStream()));
+ }
+
+ @Override
+ public ServletInputStream getInputStream() throws IOException {
+ return new ContentCachingInputStream(this.cachedContent);
+ }
+
+ private class ContentCachingInputStream extends ServletInputStream {
+
+ private ByteArrayInputStream inputStream;
+
+ public ContentCachingInputStream(byte[] bytes) {
+ this.inputStream = new ByteArrayInputStream(bytes);
+ }
+
+ @Override
+ public boolean isFinished() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isReady() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setReadListener(ReadListener readListener) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int read() throws IOException {
+ return inputStream.read();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/longfor/bff_netflix/autoconfigure/InterceptorConfig.java b/src/main/java/com/longfor/bff_netflix/autoconfigure/InterceptorConfig.java
new file mode 100644
index 0000000..f2e8e1a
--- /dev/null
+++ b/src/main/java/com/longfor/bff_netflix/autoconfigure/InterceptorConfig.java
@@ -0,0 +1,20 @@
+package com.longfor.bff_netflix.autoconfigure;
+
+import com.longfor.bff_netflix.interceptor.RequestInterceptor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+@Configuration
+public class InterceptorConfig implements WebMvcConfigurer {
+
+ @Autowired
+ private RequestInterceptor requestInterceptor;
+
+ @Override
+ public void addInterceptors(InterceptorRegistry registry) {
+// registry.addInterceptor(requestInterceptor);
+ }
+
+}
diff --git a/src/main/java/com/longfor/bff_netflix/datafetchers/ExtentDataFetcher.java b/src/main/java/com/longfor/bff_netflix/datafetchers/ExtentDataFetcher.java
index f13e709..dd4d474 100644
--- a/src/main/java/com/longfor/bff_netflix/datafetchers/ExtentDataFetcher.java
+++ b/src/main/java/com/longfor/bff_netflix/datafetchers/ExtentDataFetcher.java
@@ -27,7 +27,7 @@ public class ExtentDataFetcher {
Integer size = dfe.getExecutionStepInfo().getArgument("size");
List extents = this.extentService.extents();
return extents;
- //
+ //
// return new SimpleListConnection<>(extents).get(dfe);
}
diff --git a/src/main/java/com/longfor/bff_netflix/entity/AppSecretInfo.java b/src/main/java/com/longfor/bff_netflix/entity/AppSecretInfo.java
new file mode 100644
index 0000000..b8e3614
--- /dev/null
+++ b/src/main/java/com/longfor/bff_netflix/entity/AppSecretInfo.java
@@ -0,0 +1,12 @@
+package com.longfor.bff_netflix.entity;
+
+import lombok.Data;
+
+@Data
+public class AppSecretInfo {
+
+ private String appKey;
+
+ private String appSecret;
+
+}
diff --git a/src/main/java/com/longfor/bff_netflix/entity/SignRequest.java b/src/main/java/com/longfor/bff_netflix/entity/SignRequest.java
new file mode 100644
index 0000000..2f26980
--- /dev/null
+++ b/src/main/java/com/longfor/bff_netflix/entity/SignRequest.java
@@ -0,0 +1,49 @@
+package com.longfor.bff_netflix.entity;
+
+import lombok.Data;
+import org.apache.commons.lang3.StringUtils;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.Map;
+
+@Data
+public class SignRequest {
+
+ private String requestMethod;
+
+ private String path;
+
+ private Map params;
+
+ private String body;
+
+ private String appKey;
+
+ private String sign;
+
+ private String signMethod;
+
+ private String nonce;
+
+ private String timestamp;
+
+ public static SignRequest build(HttpServletRequest request){
+ SignRequest signRequest = new SignRequest();
+ signRequest.requestMethod = request.getMethod();
+ signRequest.path = request.getServletPath();
+ signRequest.params = request.getParameterMap();
+// signRequest.body = ""
+ signRequest.appKey = request.getHeader("x-appkey");
+ signRequest.sign = request.getHeader("x-sign");
+ signRequest.signMethod = getSignMethod(request);
+ signRequest.nonce = request.getHeader("x-nonce");
+ signRequest.timestamp = request.getHeader("x-timestamp");
+ return signRequest;
+ }
+
+ private static String getSignMethod(HttpServletRequest request){
+ final String signMethod = request.getHeader("x-sign-method");
+ return StringUtils.isBlank(signMethod) ? "MD5" : signMethod;
+ }
+
+}
diff --git a/src/main/java/com/longfor/bff_netflix/entity/ValidationResult.java b/src/main/java/com/longfor/bff_netflix/entity/ValidationResult.java
new file mode 100644
index 0000000..1278ff8
--- /dev/null
+++ b/src/main/java/com/longfor/bff_netflix/entity/ValidationResult.java
@@ -0,0 +1,9 @@
+package com.longfor.bff_netflix.entity;
+
+import lombok.Data;
+
+@Data
+public class ValidationResult {
+ private int httpStatusCode;
+ private String errorMessage;
+}
diff --git a/src/main/java/com/longfor/bff_netflix/filter/AccessFilter.java b/src/main/java/com/longfor/bff_netflix/filter/AccessFilter.java
new file mode 100644
index 0000000..9239a14
--- /dev/null
+++ b/src/main/java/com/longfor/bff_netflix/filter/AccessFilter.java
@@ -0,0 +1,117 @@
+package com.longfor.bff_netflix.filter;
+
+import com.longfor.bff_netflix.autoconfigure.ApiSignConfigLoad;
+import com.longfor.bff_netflix.autoconfigure.CachedHttpServletRequestWrapper;
+import com.longfor.bff_netflix.entity.AppSecretInfo;
+import com.longfor.bff_netflix.entity.SignRequest;
+import com.longfor.bff_netflix.entity.ValidationResult;
+import com.longfor.bff_netflix.services.SignService;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.time.DateUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.stereotype.Component;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import javax.annotation.Resource;
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.text.ParseException;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Component
+//@ComponentScan(value = {"com.longfor.bff_netflix.*"})
+public class AccessFilter extends OncePerRequestFilter {
+ private final List signServices;
+ private final ApiSignConfigLoad apiSignConfigLoad;
+ private final Map signServiceMap = new HashMap<>();
+
+ private final Map appSecretInfoMap = new HashMap<>();
+
+ public AccessFilter(ApiSignConfigLoad apiSignConfigLoad, List signServices) {
+ this.apiSignConfigLoad = apiSignConfigLoad;
+ // 1. Build SignService map
+ for (SignService signService : signServices) {
+ signServiceMap.put(signService.signMethod(), signService);
+ }
+
+ // 2. Build App Secret map
+ this.apiSignConfigLoad.loadAppSecretInfos().forEach(t -> appSecretInfoMap.put(t.getAppKey(), t));
+ this.signServices = signServices;
+ }
+
+ @Override
+ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
+ CachedHttpServletRequestWrapper wrapper = new CachedHttpServletRequestWrapper(request, "UTF-8");
+ String body = wrapper.getBodyString();
+
+ final SignRequest signRequest = SignRequest.build(request);
+ signRequest.setBody(body);
+
+ ValidationResult validationResult = validateRequest(signRequest);
+ if (validationResult.getHttpStatusCode() == HttpServletResponse.SC_OK) {
+ filterChain.doFilter(wrapper, response);
+ } else {
+ response.sendError(validationResult.getHttpStatusCode(), validationResult.getErrorMessage());
+ }
+
+ }
+
+ private ValidationResult validateRequest(SignRequest signRequest) {
+ ValidationResult result = new ValidationResult();
+ result.setHttpStatusCode(HttpServletResponse.SC_OK);
+
+ if (!apiSignConfigLoad.isSignatureValidationRequired(signRequest.getPath())) {
+ return result;
+ }
+
+ if (!checkRequest(signRequest)) {
+ result.setHttpStatusCode(HttpServletResponse.SC_BAD_REQUEST);
+ result.setErrorMessage("Required headers not specified in the request");
+ return result;
+ }
+ final AppSecretInfo appSecretInfo = appSecretInfoMap.get(signRequest.getAppKey());
+ if (appSecretInfo == null) {
+ result.setHttpStatusCode(HttpServletResponse.SC_BAD_REQUEST);
+ result.setErrorMessage("无效的应用ID");
+ return result;
+ }
+ final SignService signService = signServiceMap.get(signRequest.getSignMethod());
+ if (signService == null) {
+ result.setHttpStatusCode(HttpServletResponse.SC_BAD_REQUEST);
+ result.setErrorMessage("无效的签名方式");
+ return result;
+ }
+ final String sign = signService.sign(signRequest, appSecretInfo.getAppSecret());
+ if (!signRequest.getSign().equals(sign)) {
+ result.setHttpStatusCode(HttpServletResponse.SC_BAD_REQUEST);
+ result.setErrorMessage("无效的签名");
+ return result;
+ }
+ return result;
+ }
+
+ private boolean checkRequest(SignRequest signRequest) {
+ if (StringUtils.isAnyBlank(signRequest.getAppKey(), signRequest.getSign(), signRequest.getTimestamp(), signRequest.getNonce())) {
+// errorResponse(response, "请求头中缺失用于计算签名的参数");
+ return false;
+ }
+ try {
+ final Date tm = DateUtils.parseDate(signRequest.getTimestamp(), "yyyy-MM-dd HH:mm:ss");
+ final Date curTime = new Date();
+ if (tm.before(DateUtils.addMinutes(curTime, -10)) || tm.after(curTime)) {
+// errorResponse(response, "签名时间过期或超期");
+ return false;
+ }
+ } catch (ParseException exception) {
+// errorResponse(response, "时间戳x-timestamp格式有误");
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/src/main/java/com/longfor/bff_netflix/interceptor/RequestInterceptor.java b/src/main/java/com/longfor/bff_netflix/interceptor/RequestInterceptor.java
new file mode 100644
index 0000000..762897b
--- /dev/null
+++ b/src/main/java/com/longfor/bff_netflix/interceptor/RequestInterceptor.java
@@ -0,0 +1,116 @@
+package com.longfor.bff_netflix.interceptor;
+
+import com.alibaba.fastjson.JSON;
+import com.longfor.bff_netflix.autoconfigure.ApiSignConfigLoad;
+import com.longfor.bff_netflix.autoconfigure.CachedHttpServletRequestWrapper;
+import com.longfor.bff_netflix.entity.AppSecretInfo;
+import com.longfor.bff_netflix.entity.SignRequest;
+import com.longfor.bff_netflix.services.SignService;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.time.DateUtils;
+import org.springframework.http.MediaType;
+import org.springframework.util.StreamUtils;
+import org.springframework.web.servlet.HandlerInterceptor;
+import org.springframework.web.util.ContentCachingRequestWrapper;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.*;
+import java.nio.charset.Charset;
+import java.text.ParseException;
+import java.util.Date;
+import java.util.Map;
+
+@Slf4j
+public class RequestInterceptor implements HandlerInterceptor {
+
+ private final ApiSignConfigLoad apiSignConfigLoad;
+ private final Map signServiceMap;
+ private final Map appSecretInfoMap;
+
+ public RequestInterceptor(Map signServiceMap, Map appSecretInfoMap, ApiSignConfigLoad apiSignConfigLoad) {
+ this.signServiceMap = signServiceMap;
+ this.appSecretInfoMap = appSecretInfoMap;
+ this.apiSignConfigLoad = apiSignConfigLoad;
+ }
+
+ @Override
+ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+// final SignRequest signRequest = SignRequest.build(request);
+// CachedHttpServletRequestWrapper wrappedRequest = new CachedHttpServletRequestWrapper(request, "UTF-8");
+// BodyReaderHttpServletRequestWrapper wrappedRequest = new BodyReaderHttpServletRequestWrapper(request);
+// byte[] bytes = StreamUtils.copyToByteArray(request.getInputStream());
+// String body = new String(bytes);
+
+// if (!apiSignConfigLoad.isCheckSign(signRequest.getPath())){
+// return true;
+// }
+ return true;
+
+// if (!checkRequest(signRequest, response)){
+// return false;
+// }
+// final AppSecretInfo appSecretInfo = appSecretInfoMap.get(signRequest.getAppKey());
+// if (appSecretInfo == null){
+// errorResponse(response, "无效的应用ID");
+// return false;
+// }
+// final SignService signService = signServiceMap.get(signRequest.getSignMethod());
+// if (signService == null){
+// errorResponse(response, "无效的签名方式");
+// return false;
+// }
+// final String sign = signService.sign(signRequest, appSecretInfo.getAppSecret());
+// if (!signRequest.getSign().equals(sign)){
+// log.error("请求中的签名值 {} 与系统计算的签名值 {} 不符", signRequest.getSign(), sign);
+// errorResponse(response, "无效的签名");
+// return false;
+// }
+// return true;
+ }
+
+ /**
+ * 输出错误响应
+ */
+ private void errorResponse(HttpServletResponse response, String errorMsg) {
+ response.setStatus(HttpServletResponse.SC_OK);
+ response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
+ try (PrintWriter out = response.getWriter()) {
+ out.print(JSON.toJSONString(apiSignConfigLoad.buildErrorResult(errorMsg)));
+ out.flush();
+ } catch (Exception e) {
+ log.error("writeResponse error", e);
+ }
+ }
+
+ /**
+ * 校验请求参数基本的合法性
+ */
+ private boolean checkRequest(SignRequest signRequest, HttpServletResponse response) {
+ if (StringUtils.isAnyBlank(signRequest.getAppKey(), signRequest.getSign(), signRequest.getTimestamp(), signRequest.getNonce())) {
+ errorResponse(response, "请求头中缺失用于计算签名的参数");
+ return false;
+ }
+ try {
+ final Date tm = DateUtils.parseDate(signRequest.getTimestamp(), "yyyy-MM-dd HH:mm:ss");
+ final Date curTime = new Date();
+ if (tm.before(DateUtils.addMinutes(curTime, -10)) || tm.after(curTime)) {
+ errorResponse(response, "签名时间过期或超期");
+ return false;
+ }
+ } catch (ParseException exception) {
+ errorResponse(response, "时间戳x-timestamp格式有误");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * TODO 拦截请求重放攻击,后续再开发
+ */
+ private boolean checkRepeatedNonce(SignRequest signRequest) {
+ return false;
+ }
+
+}
diff --git a/src/main/java/com/longfor/bff_netflix/services/Md5SignServiceImpl.java b/src/main/java/com/longfor/bff_netflix/services/Md5SignServiceImpl.java
new file mode 100644
index 0000000..15f96e2
--- /dev/null
+++ b/src/main/java/com/longfor/bff_netflix/services/Md5SignServiceImpl.java
@@ -0,0 +1,23 @@
+package com.longfor.bff_netflix.services;
+
+import cn.hutool.crypto.digest.MD5;
+import com.longfor.bff_netflix.entity.SignRequest;
+import com.longfor.bff_netflix.util.ParamToStrUtil;
+import org.springframework.stereotype.Component;
+import org.springframework.stereotype.Service;
+
+@Service
+public class Md5SignServiceImpl implements SignService {
+
+ @Override
+ public String signMethod() {
+ return "MD5";
+ }
+
+ @Override
+ public String sign(SignRequest signRequest, String secretKey) {
+ final String bodyStr = ParamToStrUtil.paramToStr(signRequest.getBody(), signRequest.getParams());
+ return MD5.create().digestHex(bodyStr + secretKey + signRequest.getTimestamp() + signRequest.getNonce());
+ }
+
+}
diff --git a/src/main/java/com/longfor/bff_netflix/services/Sha256SignServiceImpl.java b/src/main/java/com/longfor/bff_netflix/services/Sha256SignServiceImpl.java
new file mode 100644
index 0000000..916a58c
--- /dev/null
+++ b/src/main/java/com/longfor/bff_netflix/services/Sha256SignServiceImpl.java
@@ -0,0 +1,24 @@
+package com.longfor.bff_netflix.services;
+
+import cn.hutool.crypto.digest.HMac;
+import cn.hutool.crypto.digest.HmacAlgorithm;
+import com.longfor.bff_netflix.entity.SignRequest;
+import com.longfor.bff_netflix.util.ParamToStrUtil;
+import org.springframework.stereotype.Component;
+import org.springframework.stereotype.Service;
+
+@Service
+public class Sha256SignServiceImpl implements SignService {
+
+ @Override
+ public String signMethod() {
+ return "SHA256";
+ }
+
+ @Override
+ public String sign(SignRequest signRequest, String secretKey) {
+ final String bodyStr = ParamToStrUtil.paramToStr(signRequest.getBody(), signRequest.getParams());
+ return new HMac(HmacAlgorithm.HmacSHA256).digestHex(bodyStr + secretKey + signRequest.getTimestamp() + signRequest.getNonce());
+ }
+
+}
diff --git a/src/main/java/com/longfor/bff_netflix/services/SignService.java b/src/main/java/com/longfor/bff_netflix/services/SignService.java
new file mode 100644
index 0000000..bb03ca4
--- /dev/null
+++ b/src/main/java/com/longfor/bff_netflix/services/SignService.java
@@ -0,0 +1,17 @@
+package com.longfor.bff_netflix.services;
+
+import com.longfor.bff_netflix.entity.SignRequest;
+
+public interface SignService {
+
+ /**
+ * 签名方式
+ */
+ String signMethod();
+
+ /**
+ * 计算签名
+ */
+ String sign(SignRequest signRequest, String secretKey);
+
+}
diff --git a/src/main/java/com/longfor/bff_netflix/util/ParamToStrUtil.java b/src/main/java/com/longfor/bff_netflix/util/ParamToStrUtil.java
new file mode 100644
index 0000000..a651d2f
--- /dev/null
+++ b/src/main/java/com/longfor/bff_netflix/util/ParamToStrUtil.java
@@ -0,0 +1,49 @@
+package com.longfor.bff_netflix.util;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.TypeReference;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.util.CollectionUtils;
+
+import java.net.URLEncoder;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+@Slf4j
+public class ParamToStrUtil {
+
+ private ParamToStrUtil(){}
+
+ public static String paramToStr(String body, Map urlParams) {
+ Map data = new HashMap<>();
+ if (StringUtils.isNotBlank(body)){
+ data.putAll(JSON.parseObject(body, new TypeReference