remove RequestInterceptor, add tracingInstrumentation

This commit is contained in:
renxiaoyin 2023-04-06 19:00:09 +08:00
parent 5245e52328
commit c1a640b1d9
9 changed files with 89 additions and 311 deletions

View File

@ -1,47 +0,0 @@
package com.longfor.c2.graphql.starter.autoconfigure;
import com.longfor.c2.graphql.starter.entity.AppSecretInfo;
import com.longfor.c2.graphql.starter.interceptor.RequestInterceptor;
import com.longfor.c2.graphql.starter.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<SignService> signServices;
@Resource
private ApiSignConfigLoad apiSignConfigLoad;
@Bean
public RequestInterceptor requestInterceptor(){
final Map<String, SignService> signServiceMap = new HashMap<>();
final Map<String, AppSecretInfo> 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);
}
}

View File

@ -14,7 +14,7 @@ import org.slf4j.LoggerFactory;
import java.nio.charset.Charset;
/**
* @author rx6
* @author rxy
*/
public class CachedHttpServletRequestWrapper extends HttpServletRequestWrapper {
private final byte[] cachedContent;

View File

@ -1,127 +0,0 @@
package com.longfor.c2.graphql.starter.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: 复制输入流</br>
*
* @param inputStream
* @return</br>
*/
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();
}
}
}

View File

@ -1,20 +0,0 @@
package com.longfor.c2.graphql.starter.autoconfigure;
import com.longfor.c2.graphql.starter.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);
}
}

View File

@ -23,8 +23,5 @@ public class ExtentDataFetcher {
Integer size = dfe.getExecutionStepInfo().getArgument("size");
List<Extent> extents = this.extentService.extents();
return extents;
//
// return new SimpleListConnection<>(extents).get(dfe);
}
}

View File

@ -5,11 +5,13 @@ import com.longfor.types.Shop;
import com.netflix.graphql.dgs.DgsComponent;
import com.netflix.graphql.dgs.DgsQuery;
import com.netflix.graphql.dgs.InputArgument;
import lombok.extern.slf4j.Slf4j;
import java.util.List;
import java.util.stream.Collectors;
@DgsComponent
@Slf4j
public class ShopDataFetcher {
private final ShopService shopService;
@ -26,6 +28,7 @@ public class ShopDataFetcher {
if (shopNameFilter == null) {
return shopService.shops();
}
log.info("Query shops: shopNameFilter={}", shopNameFilter);
return shopService.shops().stream().collect(Collectors.toList());
}

View File

@ -22,7 +22,6 @@ import java.util.List;
import java.util.Map;
@Component
//@ComponentScan(value = {"com.longfor.bff_netflix.*"})
public class AccessFilter extends OncePerRequestFilter {
private final List<SignService> signServices;
private final ApiSignConfigLoad apiSignConfigLoad;

View File

@ -0,0 +1,85 @@
package com.longfor.c2.graphql.starter.instrumentation;
import graphql.ExecutionResult;
import graphql.execution.instrumentation.InstrumentationContext;
import graphql.execution.instrumentation.InstrumentationState;
import graphql.execution.instrumentation.SimpleInstrumentation;
import graphql.execution.instrumentation.parameters.InstrumentationExecutionParameters;
import graphql.execution.instrumentation.parameters.InstrumentationFieldFetchParameters;
import graphql.schema.DataFetcher;
import graphql.schema.GraphQLNonNull;
import graphql.schema.GraphQLObjectType;
import graphql.schema.GraphQLOutputType;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.concurrent.CompletableFuture;
@Component
@Slf4j
public class TracingInstrumentation extends SimpleInstrumentation {
@Override
public InstrumentationState createState() {
return new TracingState();
}
@Override
public InstrumentationContext<ExecutionResult> beginExecution(InstrumentationExecutionParameters parameters) {
TracingState tracingState = parameters.getInstrumentationState();
tracingState.startTime = System.currentTimeMillis();
return super.beginExecution(parameters);
}
@Override
public DataFetcher<?> instrumentDataFetcher(DataFetcher<?> dataFetcher, InstrumentationFieldFetchParameters parameters) {
// We only care about user code
if(parameters.isTrivialDataFetcher()) {
return dataFetcher;
}
return environment -> {
long startTime = System.currentTimeMillis();
Object result = dataFetcher.get(environment);
if(result instanceof CompletableFuture) {
((CompletableFuture<?>) result).whenComplete((r, ex) -> {
long totalTime = System.currentTimeMillis() - startTime;
log.info("Async datafetcher {} took {}ms", findDatafetcherTag(parameters), totalTime);
});
} else {
long totalTime = System.currentTimeMillis() - startTime;
log.info("Datafetcher {} took {}ms", findDatafetcherTag(parameters), totalTime);
}
return result;
};
}
@Override
public CompletableFuture<ExecutionResult> instrumentExecutionResult(ExecutionResult executionResult, InstrumentationExecutionParameters parameters) {
TracingState tracingState = parameters.getInstrumentationState();
long totalTime = System.currentTimeMillis() - tracingState.startTime;
log.info("Total execution time: {}ms", totalTime);
return super.instrumentExecutionResult(executionResult, parameters);
}
private String findDatafetcherTag(InstrumentationFieldFetchParameters parameters) {
GraphQLOutputType type = parameters.getExecutionStepInfo().getParent().getType();
GraphQLObjectType parent;
if (type instanceof GraphQLNonNull) {
parent = (GraphQLObjectType) ((GraphQLNonNull) type).getWrappedType();
} else {
parent = (GraphQLObjectType) type;
}
return parent.getName() + "." + parameters.getExecutionStepInfo().getPath().getSegmentName();
}
static class TracingState implements InstrumentationState {
long startTime;
}
}

View File

@ -1,112 +0,0 @@
package com.longfor.c2.graphql.starter.interceptor;
import com.alibaba.fastjson.JSON;
import com.longfor.c2.graphql.starter.autoconfigure.ApiSignConfigLoad;
import com.longfor.c2.graphql.starter.entity.AppSecretInfo;
import com.longfor.c2.graphql.starter.entity.SignRequest;
import com.longfor.c2.graphql.starter.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.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
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<String, SignService> signServiceMap;
private final Map<String, AppSecretInfo> appSecretInfoMap;
public RequestInterceptor(Map<String, SignService> signServiceMap, Map<String, AppSecretInfo> 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;
}
}