remove RequestInterceptor, add tracingInstrumentation
This commit is contained in:
parent
5245e52328
commit
c1a640b1d9
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue