|
@@ -6,10 +6,13 @@ import cn.hutool.core.map.MapUtil;
|
|
|
import cn.hutool.core.util.ObjUtil;
|
|
|
import cn.hutool.core.util.StrUtil;
|
|
|
import cn.hutool.crypto.digest.DigestUtil;
|
|
|
+import com.alibaba.fastjson.JSON;
|
|
|
import com.citu.framework.common.exception.ServiceException;
|
|
|
import com.citu.framework.common.util.servlet.ServletUtils;
|
|
|
import com.citu.framework.signature.core.annotation.ApiSignature;
|
|
|
import com.citu.framework.signature.core.redis.ApiSignatureRedisDAO;
|
|
|
+import com.citu.module.menduner.system.api.error.ErrorRecordApi;
|
|
|
+import com.citu.module.menduner.system.api.error.ErrorRecordReqDTO;
|
|
|
import lombok.AllArgsConstructor;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import org.aspectj.lang.JoinPoint;
|
|
@@ -34,6 +37,11 @@ public class ApiSignatureAspect {
|
|
|
|
|
|
private final ApiSignatureRedisDAO signatureRedisDAO;
|
|
|
|
|
|
+ private final ErrorRecordApi errorRecordApi;
|
|
|
+
|
|
|
+ private static final ThreadLocal<Map<String,Object>> recordThreadLocal = new ThreadLocal<>();
|
|
|
+
|
|
|
+
|
|
|
/**
|
|
|
* 获取请求头加签参数 Map
|
|
|
*
|
|
@@ -56,7 +64,7 @@ public class ApiSignatureAspect {
|
|
|
* @return queryParams
|
|
|
*/
|
|
|
private static SortedMap<String, String> getRequestParameterMap(HttpServletRequest request) {
|
|
|
- SortedMap<String, String> sortedMap = new TreeMap<>();
|
|
|
+ SortedMap<String, String> sortedMap = new TreeMap<>();
|
|
|
for (Map.Entry<String, String[]> entry : request.getParameterMap().entrySet()) {
|
|
|
sortedMap.put(entry.getKey(), entry.getValue()[0]);
|
|
|
}
|
|
@@ -74,9 +82,16 @@ public class ApiSignatureAspect {
|
|
|
@Before("@annotation(signature)")
|
|
|
public void beforePointCut(JoinPoint joinPoint, ApiSignature signature) {
|
|
|
// 1. 验证通过,直接结束
|
|
|
+ recordThreadLocal.set(new HashMap<>());
|
|
|
if (verifySignature(signature, Objects.requireNonNull(ServletUtils.getRequest()))) {
|
|
|
+ recordThreadLocal.remove();
|
|
|
return;
|
|
|
}
|
|
|
+ errorRecordApi.create(ErrorRecordReqDTO.builder()
|
|
|
+ .mark(ServletUtils.getRequest().getHeader(signature.timestamp()))
|
|
|
+ .content(JSON.toJSONString(recordThreadLocal.get()))
|
|
|
+ .build());
|
|
|
+ recordThreadLocal.remove();
|
|
|
|
|
|
// 2. 验证不通过,抛出异常
|
|
|
log.error("[beforePointCut][方法{} 参数({}) 签名失败]", joinPoint.getSignature().toString(),
|
|
@@ -93,13 +108,18 @@ public class ApiSignatureAspect {
|
|
|
// 1.2 校验 appId 是否能获取到对应的 appSecret
|
|
|
String appId = request.getHeader(signature.appId());
|
|
|
String appSecret = signatureRedisDAO.getAppSecret(appId);
|
|
|
+ recordThreadLocal.get().put("appSecret", appSecret);
|
|
|
Assert.notNull(appSecret, "[appId({})] 找不到对应的 appSecret", appId);
|
|
|
|
|
|
// 2. 校验签名【重要!】
|
|
|
String clientSignature = request.getHeader(signature.sign()); // 客户端签名
|
|
|
String serverSignatureString = buildSignatureString(signature, request, appSecret); // 服务端签名字符串
|
|
|
String serverSignature = DigestUtil.sha256Hex(serverSignatureString); // 服务端签名
|
|
|
+ recordThreadLocal.get().put("clientSignature", clientSignature);
|
|
|
+ recordThreadLocal.get().put("serverSignatureString", serverSignatureString);
|
|
|
+ recordThreadLocal.get().put("serverSignature", serverSignature);
|
|
|
if (ObjUtil.notEqual(clientSignature, serverSignature)) {
|
|
|
+ recordThreadLocal.get().put("reason", "ObjUtil.notEqual(clientSignature, serverSignature)");
|
|
|
return false;
|
|
|
}
|
|
|
|
|
@@ -124,19 +144,27 @@ public class ApiSignatureAspect {
|
|
|
private boolean verifyHeaders(ApiSignature signature, HttpServletRequest request) {
|
|
|
// 1. 非空校验
|
|
|
String appId = request.getHeader(signature.appId());
|
|
|
+ recordThreadLocal.get().put("appId", appId);
|
|
|
if (StrUtil.isBlank(appId)) {
|
|
|
+ recordThreadLocal.get().put("reason", "appId is null");
|
|
|
return false;
|
|
|
}
|
|
|
String timestamp = request.getHeader(signature.timestamp());
|
|
|
+ recordThreadLocal.get().put("timestamp", timestamp);
|
|
|
if (StrUtil.isBlank(timestamp)) {
|
|
|
+ recordThreadLocal.get().put("reason", "timestamp is null");
|
|
|
return false;
|
|
|
}
|
|
|
String nonce = request.getHeader(signature.nonce());
|
|
|
+ recordThreadLocal.get().put("nonce", nonce);
|
|
|
if (StrUtil.length(nonce) < 10) {
|
|
|
+ recordThreadLocal.get().put("reason", "timestamp length < 10");
|
|
|
return false;
|
|
|
}
|
|
|
String sign = request.getHeader(signature.sign());
|
|
|
+ recordThreadLocal.get().put("sign", sign);
|
|
|
if (StrUtil.isBlank(sign)) {
|
|
|
+ recordThreadLocal.get().put("reason", "sign is null");
|
|
|
return false;
|
|
|
}
|
|
|
|
|
@@ -144,12 +172,20 @@ public class ApiSignatureAspect {
|
|
|
long expireTime = signature.timeUnit().toMillis(signature.timeout());
|
|
|
long requestTimestamp = Long.parseLong(timestamp);
|
|
|
long timestampDisparity = Math.abs(System.currentTimeMillis() - requestTimestamp);
|
|
|
+ recordThreadLocal.get().put("expireTime", expireTime);
|
|
|
+ recordThreadLocal.get().put("requestTimestamp", requestTimestamp);
|
|
|
+ recordThreadLocal.get().put("timestampDisparity", timestampDisparity);
|
|
|
if (timestampDisparity > expireTime) {
|
|
|
+ recordThreadLocal.get().put("reason", "timestampDisparity > expireTime 超出范围");
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
// 3. 检查 nonce 是否存在,有且仅能使用一次
|
|
|
- return signatureRedisDAO.getNonce(appId, nonce) == null;
|
|
|
+ boolean result = signatureRedisDAO.getNonce(appId, nonce) == null;
|
|
|
+ if(!result) {
|
|
|
+ recordThreadLocal.get().put("reason", "signatureRedisDAO.getNonce(appId, nonce) != null");
|
|
|
+ }
|
|
|
+ return result;
|
|
|
}
|
|
|
|
|
|
/**
|