diff --git a/web-adapter/pom.xml b/web-adapter/pom.xml
index ad4ea89d..74b50006 100644
--- a/web-adapter/pom.xml
+++ b/web-adapter/pom.xml
@@ -30,5 +30,9 @@
com.jjb.saas
jjb-saas-framework-job
+
+ org.springframework.boot
+ spring-boot-starter-aop
+
diff --git a/web-adapter/src/main/java/com/zcloud/basic/info/aspect/SyncResponseEncryptAspect.java b/web-adapter/src/main/java/com/zcloud/basic/info/aspect/SyncResponseEncryptAspect.java
new file mode 100644
index 00000000..887f2e1b
--- /dev/null
+++ b/web-adapter/src/main/java/com/zcloud/basic/info/aspect/SyncResponseEncryptAspect.java
@@ -0,0 +1,221 @@
+package com.zcloud.basic.info.aspect;
+
+import com.alibaba.cola.dto.SingleResponse;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.zcloud.basic.info.persistence.dataobject.CorpInfoKeyDO;
+import com.zcloud.basic.info.service.CorpInfoKeyService;
+import com.zcloud.basic.info.utils.SyncPageResponse;
+import com.zcloud.gbscommon.utils.Sm2Util;
+import lombok.extern.slf4j.Slf4j;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+//@Aspect
+//@Component
+@Slf4j
+public class SyncResponseEncryptAspect {
+
+ @Autowired
+ private CorpInfoKeyService corpInfoKeyService;
+
+ private static final long TIMESTAMP_TOLERANCE = 5 * 60 * 1000L;
+
+ @Pointcut("execution(* com.zcloud.basic.info.web.sync.SyncController.*(..))")
+ public void syncControllerPointcut() {}
+
+ @Around("syncControllerPointcut()")
+ public Object encrypt(ProceedingJoinPoint joinPoint) throws Throwable {
+ HttpServletRequest request = getHttpServletRequest();
+
+ String corpinfoId = request.getHeader("X-CORPINFO-ID");
+ String timestamp = request.getHeader("X-TIMESTAMP");
+ String signature = request.getHeader("X-SIGNATURE");
+
+ if (corpinfoId == null || timestamp == null || signature == null) {
+ throw new SecurityException("缺少必要的认证头信息");
+ }
+
+ long requestTime = Long.parseLong(timestamp);
+// if (Math.abs(System.currentTimeMillis() - requestTime) > TIMESTAMP_TOLERANCE) {
+// throw new SecurityException("请求已过期");
+// }
+
+ CorpInfoKeyDO keyDO = corpInfoKeyService.getByCorpinfoId(Long.parseLong(corpinfoId));
+ if (keyDO == null) {
+ throw new SecurityException("未找到对应的密钥配置");
+ }
+ if (keyDO.getPublicKey() == null || keyDO.getSignKey() == null) {
+ throw new SecurityException("密钥配置不完整");
+ }
+
+ String body = getRequestBody(joinPoint);
+
+ if (!sm2Verify(signature, corpinfoId, timestamp, body, keyDO)) {
+ throw new SecurityException("签名验证失败");
+ }
+
+ Object result = joinPoint.proceed();
+ if (result == null) {
+ return result;
+ }
+
+ String className = result.getClass().getName();
+ if (className.contains("SingleResponse")) {
+ SingleResponse response = (SingleResponse) result;
+ Object data = response.getData();
+ if (data != null) {
+ response.setData(encryptData(data, keyDO.getPublicKey()));
+ }
+ } else if (className.contains("SyncPageResponse") || className.contains("PageResponse") || className.contains("MultiResponse")) {
+ SyncPageResponse response = (SyncPageResponse) result;
+ Object data = response.getData();
+ if (data != null) {
+ response.setData(encryptData(data, keyDO.getPublicKey()));
+ }
+ }
+
+ return result;
+ }
+
+ private HttpServletRequest getHttpServletRequest() {
+ return ((org.springframework.web.context.request.ServletRequestAttributes)
+ org.springframework.web.context.request.RequestContextHolder.getRequestAttributes())
+ .getRequest();
+ }
+
+ private String getRequestBody(ProceedingJoinPoint joinPoint) {
+ Object[] args = joinPoint.getArgs();
+ if (args != null && args.length > 0) {
+ for (Object arg : args) {
+ if (arg != null && !(arg instanceof HttpServletRequest)) {
+ return JSON.toJSONString(arg);
+ }
+ }
+ }
+ return "";
+ }
+
+private boolean sm2Verify(String signature, String corpinfoId, String timestamp, String body, CorpInfoKeyDO keyDO) {
+ try {
+ String decrypted = Sm2Util.decryptBase64(signature, keyDO.getSignPrivateKey());
+ JSONObject json = JSON.parseObject(decrypted);
+
+ String jsonCorpinfoId = json.getString("corpinfoId");
+ String jsonTimestamp = json.getString("timestamp");
+ String jsonBody = json.getString("body");
+
+ if (!corpinfoId.equals(jsonCorpinfoId)) {
+ log.warn("corpinfoId不匹配: expected={}, actual={}", corpinfoId, jsonCorpinfoId);
+ return false;
+ }
+ if (!timestamp.equals(jsonTimestamp)) {
+ log.warn("timestamp不匹配: expected={}, actual={}", timestamp, jsonTimestamp);
+ return false;
+ }
+ // 将body 和 jsonBody 都转换成 JSONObject 并比较其中的字段是否相同,值是否相同
+ JSONObject jsonBodyObject = JSON.parseObject(jsonBody);
+ JSONObject bodyObject = JSON.parseObject(body);
+
+ if (!isJsonEqualsIgnoreArrayOrder(jsonBodyObject, bodyObject)) {
+ log.warn("body不匹配: expected={}, actual={}", body, jsonBody);
+ return false;
+ }
+ return true;
+ } catch (Exception e) {
+ log.error("SM2解密或验证异常: {}", e.getMessage());
+ return false;
+ }
+ }
+ private Object encryptData(Object data, String publicKey) {
+ return Sm2Util.encryptHex(JSON.toJSONString(data), publicKey);
+ }
+
+ private boolean isJsonEqualsIgnoreArrayOrder(Object obj1, Object obj2) {
+ // 都是 null
+ if (obj1 == null && obj2 == null) {
+ return true;
+ }
+ // 一个 null 一个不是
+ if (obj1 == null || obj2 == null) {
+ return false;
+ }
+
+ // 类型不一样
+ if (!obj1.getClass().equals(obj2.getClass())) {
+ return false;
+ }
+
+ // 如果是 JSONObject
+ if (obj1 instanceof JSONObject) {
+ JSONObject json1 = (JSONObject) obj1;
+ JSONObject json2 = (JSONObject) obj2;
+
+ // 对比 key 集合是否一致
+ if (!json1.keySet().equals(json2.keySet())) {
+ return false;
+ }
+
+ // 递归对比每个 value
+ for (String key : json1.keySet()) {
+ Object val1 = json1.get(key);
+ Object val2 = json2.get(key);
+ if (!isJsonEqualsIgnoreArrayOrder(val1, val2)) {
+ return false;
+ }
+ }
+ return true;
+ } else if (obj1 instanceof JSONArray) {
+ JSONArray arr1 = (JSONArray) obj1;
+ JSONArray arr2 = (JSONArray) obj2;
+
+ if (arr1.size() != arr2.size()) {
+ return false;
+ }
+
+ // 转成 List 后排序
+ List