|
@@ -1,18 +1,98 @@
|
|
package com.smppw.analysis.infrastructure.components;
|
|
package com.smppw.analysis.infrastructure.components;
|
|
|
|
|
|
|
|
+import cn.hutool.core.util.StrUtil;
|
|
|
|
+import cn.hutool.crypto.SignUtil;
|
|
|
|
+import cn.hutool.json.JSONUtil;
|
|
|
|
+import com.smppw.analysis.infrastructure.config.AnalysisProperty;
|
|
|
|
+import com.smppw.common.pojo.ResultVo;
|
|
import jakarta.servlet.http.HttpServletRequest;
|
|
import jakarta.servlet.http.HttpServletRequest;
|
|
import jakarta.servlet.http.HttpServletResponse;
|
|
import jakarta.servlet.http.HttpServletResponse;
|
|
|
|
+import org.slf4j.Logger;
|
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
|
+import org.springframework.http.MediaType;
|
|
|
|
+import org.springframework.lang.NonNull;
|
|
import org.springframework.web.servlet.HandlerInterceptor;
|
|
import org.springframework.web.servlet.HandlerInterceptor;
|
|
|
|
|
|
|
|
+import java.nio.charset.Charset;
|
|
|
|
+import java.time.Duration;
|
|
|
|
+import java.util.Map;
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* @author wangzaijun
|
|
* @author wangzaijun
|
|
* @date 2023/8/7 15:58
|
|
* @date 2023/8/7 15:58
|
|
* @description api验证签名拦截器
|
|
* @description api验证签名拦截器
|
|
*/
|
|
*/
|
|
public class ApiSignInterceptor implements HandlerInterceptor {
|
|
public class ApiSignInterceptor implements HandlerInterceptor {
|
|
|
|
+ private static final long MAX_TIMEOUT = 50000L;
|
|
|
|
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
|
|
|
+ private final AnalysisProperty property;
|
|
|
|
+
|
|
|
|
+ public ApiSignInterceptor(AnalysisProperty property) {
|
|
|
|
+ this.property = property;
|
|
|
|
+ }
|
|
|
|
+
|
|
@Override
|
|
@Override
|
|
- public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
|
|
|
- // todo 参数签名验证
|
|
|
|
|
|
+ public boolean preHandle(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull Object handler) throws Exception {
|
|
|
|
+ if (logger.isInfoEnabled()) {
|
|
|
|
+ logger.info(StrUtil.format("{} 接口正在被 {} 请求!", request.getRequestURI(), request.getRemoteAddr()));
|
|
|
|
+ }
|
|
|
|
+ if (!this.property.getEnableSign()) {
|
|
|
|
+ if (logger.isDebugEnabled()) {
|
|
|
|
+ logger.debug("所有接口不需要签名验证!");
|
|
|
|
+ }
|
|
|
|
+ return HandlerInterceptor.super.preHandle(request, response, handler);
|
|
|
|
+ }
|
|
|
|
+ String sign = request.getHeader("sign");
|
|
|
|
+ if (StrUtil.isBlank(sign)) {
|
|
|
|
+ this.writeFailJson(response, "sign 参数为空");
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ String timestamp = request.getHeader("timestamp");
|
|
|
|
+ if (StrUtil.isBlank(timestamp)) {
|
|
|
|
+ this.writeFailJson(response, "timestamp 参数为空");
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ String appKey = request.getParameter("appKey");
|
|
|
|
+ AnalysisProperty.AppSign appSign = this.property.getAppSigns().stream().filter(e -> appKey.equals(e.getAppKey())).findFirst().orElse(null);
|
|
|
|
+ if (appSign == null) {
|
|
|
|
+ this.writeFailJson(response, "appKey非法");
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ if (!appSign.getEnabled()) {
|
|
|
|
+ logger.warn(StrUtil.format("{} 应用未开启接口签名验证!", appKey));
|
|
|
|
+ return HandlerInterceptor.super.preHandle(request, response, handler);
|
|
|
|
+ }
|
|
|
|
+ boolean flag = this.checkTimestamp(request, response, timestamp, appSign.getTimeout());
|
|
|
|
+ if (!flag) {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ String appSecret = appSign.getAppSecret();
|
|
|
|
+ Map<String, String[]> parameterMap = request.getParameterMap();
|
|
|
|
+ String paramsSign = SignUtil.signParamsMd5(parameterMap, appSecret, timestamp);
|
|
|
|
+ if (!sign.equalsIgnoreCase(paramsSign)) {
|
|
|
|
+ this.writeFailJson(response, "签名验证失败");
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
return HandlerInterceptor.super.preHandle(request, response, handler);
|
|
return HandlerInterceptor.super.preHandle(request, response, handler);
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ private boolean checkTimestamp(HttpServletRequest request, HttpServletResponse response, String timestamp, Duration timeout) throws Exception {
|
|
|
|
+ long millis = System.currentTimeMillis();
|
|
|
|
+ long time = Long.parseLong(timestamp);
|
|
|
|
+ long seconds = Math.min(timeout.getSeconds(), MAX_TIMEOUT);
|
|
|
|
+ if (millis - time > seconds) {
|
|
|
|
+ logger.error(StrUtil.format("接口有重放攻击,调用方:{}-{}"), request.getRemoteUser(), request.getRemoteAddr());
|
|
|
|
+ this.writeFailJson(response, "请求失败!");
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private synchronized void writeFailJson(HttpServletResponse response, String msg) throws Exception {
|
|
|
|
+ ResultVo<?> resultVo = ResultVo.fail(400, msg);
|
|
|
|
+ response.setContentType(MediaType.APPLICATION_JSON_VALUE);
|
|
|
|
+ response.setCharacterEncoding(Charset.defaultCharset().displayName());
|
|
|
|
+ response.getWriter().write(JSONUtil.toJsonStr(resultVo));
|
|
|
|
+ response.getWriter().flush();
|
|
|
|
+ }
|
|
}
|
|
}
|