From 31ca9c1384a1f07976917f37ee1815f0ca815433 Mon Sep 17 00:00:00 2001 From: vincent Date: Thu, 5 Nov 2026 17:48:07 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AF=86=E8=AF=84=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/supervision/CommonController.java | 28 ++++ .../job/CryptoSensitiveOperXzzfJob.java | 28 ++++ .../service/ICryptoService.java | 41 +++++ .../impl/CryptoServiceServiceImpl.java | 158 ++++++++++++++++++ .../service/ICryptoSensitiveHsmService.java | 10 ++ .../impl/CryptoSensitiveHsmServiceImpl.java | 12 +- 6 files changed, 272 insertions(+), 5 deletions(-) create mode 100644 zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/quartz/job/CryptoSensitiveOperXzzfJob.java create mode 100644 zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/ICryptoService.java create mode 100644 zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/CryptoServiceServiceImpl.java diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/web/supervision/CommonController.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/web/supervision/CommonController.java index 60537df4..f94a48a7 100644 --- a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/web/supervision/CommonController.java +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/web/supervision/CommonController.java @@ -2,6 +2,7 @@ package com.zdxt.enforcementcode.controller.web.supervision; import com.zdxt.enforcementcode.common.util.ZwSmsClient; import com.zdxt.enforcementcode.domain.bo.thirdparty.SIQMsgParamBo; +import com.zdxt.enforcementcode.service.ICryptoService; import com.zdxt.enforcementcode.service.thirdparty.ISIQService; import jakarta.validation.constraints.NotBlank; import lombok.RequiredArgsConstructor; @@ -33,6 +34,7 @@ public class CommonController extends BaseController { private final ZwSmsClient zwSmsClient; private final ISIQService siqService; private final ICryptoSensitiveHsmService cryptoSensitiveHsmService; + private final ICryptoService cryptoService; /** * 直接发送短信(内容写死,用于联调测试) @@ -124,6 +126,32 @@ public class CommonController extends BaseController { } + /** + * 行政执法数据完整性校验 + * @return + */ + @GetMapping("/syncIntegrityHashXzzf") + public R> syncIntegrityHashXzzf() { + String[] tables = {"sys_user", "sys_oper_log"}; + for (String table : tables) { + log.info("数据完整性校验表开始 {}", table); + int processed = cryptoService.syncIntegrityHashXzzf(); + log.info("数据完整性校验表结束 {}, 处理记录数: {}", table, processed); + } + return R.ok("同步用户密评成功"); + } + + /** + * 数据完整性校验 + * @return + */ + @GetMapping("/verifyXzzfInfo") + public R> verifyXzzfInfo() { + log.info("数据完整性校验表开始 enforcement_registration" ); + int processed = cryptoService.verifyXzzfInfo(); + log.info("数据完整性校验表结束 enforcement_registration, 处理记录数: {}", processed); + return R.ok("同步用户密评成功"); + } } diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/quartz/job/CryptoSensitiveOperXzzfJob.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/quartz/job/CryptoSensitiveOperXzzfJob.java new file mode 100644 index 00000000..7b53de91 --- /dev/null +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/quartz/job/CryptoSensitiveOperXzzfJob.java @@ -0,0 +1,28 @@ +package com.zdxt.enforcementcode.quartz.job; + +import com.zdxt.enforcementcode.service.ICryptoService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * 密码平台 + *

行政执法检查数据一致性校验

+ * + * @author zrb + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class CryptoSensitiveOperXzzfJob { + private final ICryptoService cryptoService; + public void execute(){ + this.verifyXzzfInfo(); + } + + public void verifyXzzfInfo(){ + log.info("行政执法检查数据一致性校验开始"); + cryptoService.verifyXzzfInfo(); + log.info("行政执法检查数据一致性校验结束"); + } +} diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/ICryptoService.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/ICryptoService.java new file mode 100644 index 00000000..f76ec028 --- /dev/null +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/ICryptoService.java @@ -0,0 +1,41 @@ +package com.zdxt.enforcementcode.service; + +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 密码服务 + * + * @author Lion Li + * @date 2026-05-25 + */ +public interface ICryptoService { + + /** + * 同步前一天有变更的执法登记(enforcement_registration)数据完整性摘要 + *

+ * 查询 update_time 落在前一天范围内的记录(is_delete=0),以数据库中实际存储值 + * (enterprise_id、law_enforcer_id、law_enforcement_item_text)计算 HMAC-SM3 摘要, + * 按 upsert 策略写入 crypto_integrity_data 表,供后续完整性校验比对。 + * + * @return 成功处理的记录数 + */ + int syncIntegrityHashXzzf(); + + /** + * 校验执法登记(enforcement_registration)数据完整性摘要 + *

+ * 查询 crypto_integrity_data 中 table_name=enforcement_registration 且 generate_time 早于今天的 + * 基准摘要记录,回查业务表当前数据重算摘要并比对: + * 一致标记 verify_result=1(通过),不一致标记 verify_result=2(篡改), + * 校验异常标记 verify_result=4(校验异常, 非篡改),数据不存在或已删除标记 verify_result=3(跳过), + * 同时刷新 last_verify_time。 + * + * @return 完成校验的记录数 + */ + int verifyXzzfInfo(); + +} diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/CryptoServiceServiceImpl.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/CryptoServiceServiceImpl.java new file mode 100644 index 00000000..5b100737 --- /dev/null +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/CryptoServiceServiceImpl.java @@ -0,0 +1,158 @@ +package com.zdxt.enforcementcode.service.impl; + +import cn.hutool.core.date.DateUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.zdxt.enforcementcode.domain.EnforcementRegistration; +import com.zdxt.enforcementcode.mapper.EnforcementRegistrationMapper; +import com.zdxt.enforcementcode.service.ICryptoService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.helper.DataPermissionHelper; +import org.dromara.system.domain.bean.CryptoIntegrityData; +import org.dromara.system.mapper.CryptoIntegrityDataMapper; +import org.dromara.system.service.ICryptoSensitiveHsmService; +import org.springframework.stereotype.Service; + +import java.util.Date; + +/** + * 新增密码服务实现 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class CryptoServiceServiceImpl implements ICryptoService { + + private final ICryptoSensitiveHsmService cryptoSensitiveHsmService; + private final EnforcementRegistrationMapper enforcementRegistrationMapper; + private final CryptoIntegrityDataMapper cryptoIntegrityDataMapper; + + private static final String TABLE_ENFORCEMENT_REGISTRATION = "enforcement_registration"; + + /** + * 摘要字段口径: 使用数据库列名, 与 sys_oper_log 基线风格一致 + */ + private static final String ER_FIELD_LIST = "enterprise_id,law_enforcer_id,law_enforcement_item_text"; + + @Override + public int syncIntegrityHashXzzf() { + Date begin = DateUtil.beginOfDay(DateUtil.yesterday()); + Date end = DateUtil.endOfDay(DateUtil.yesterday()); + + int processed = 0; + long pageSize = 500L; + Page page = new Page<>(1, pageSize); + while (true) { + Page result = DataPermissionHelper.ignore(() -> + enforcementRegistrationMapper.selectPage(page, new LambdaQueryWrapper() + .select(EnforcementRegistration::getId, EnforcementRegistration::getEnterpriseId, + EnforcementRegistration::getLawEnforcerId, EnforcementRegistration::getLawEnforcementItemText) + .eq(EnforcementRegistration::getIsDelete, 0) + .eq(EnforcementRegistration::getIsSubmit, 1) + .ge(EnforcementRegistration::getUpdateTime, begin) + .le(EnforcementRegistration::getUpdateTime, end) + .orderByAsc(EnforcementRegistration::getId))); + for (EnforcementRegistration er : result.getRecords()) { + try { + cryptoSensitiveHsmService.saveIntegrityHash(TABLE_ENFORCEMENT_REGISTRATION, er.getId(), ER_FIELD_LIST, + StringUtils.isNotBlank(er.getEnterpriseId()) ? er.getEnterpriseId() : "", + StringUtils.isNotBlank(er.getLawEnforcerId()) ? er.getLawEnforcerId() : "", + StringUtils.isNotBlank(er.getLawEnforcementItemText()) ? er.getLawEnforcementItemText() : ""); + processed++; + } catch (Exception e) { + log.warn("同步前一天修改执法登记完整性摘要失败, id={}", er.getId(), e); + } + } + if (!result.hasNext()) { + break; + } + page.setCurrent(page.getCurrent() + 1); + } + log.info("同步前一天修改执法登记完整性摘要完成, 处理数量={}", processed); + return processed; + } + + @Override + public int verifyXzzfInfo() { + Date today = DateUtil.beginOfDay(new Date()); + + int processed = 0, passed = 0, tampered = 0, skipped = 0, errored = 0; + long pageSize = 500L; + Page page = new Page<>(1, pageSize); + LambdaQueryWrapper lqw = new LambdaQueryWrapper() + .eq(CryptoIntegrityData::getTableName, TABLE_ENFORCEMENT_REGISTRATION) + .eq(CryptoIntegrityData::getIsDeleted, 0) + .lt(CryptoIntegrityData::getGenerateTime, today); + while (true) { + Page result = DataPermissionHelper.ignore( + () -> cryptoIntegrityDataMapper.selectPage(page, lqw)); + for (CryptoIntegrityData record : result.getRecords()) { + try { + EnforcementRegistration er = DataPermissionHelper.ignore( + () -> enforcementRegistrationMapper.selectById(record.getBizId())); + if (er == null || (er.getIsDelete() != null && er.getIsDelete() == 1)) { + record.setVerifyResult(3); + skipped++; + } else { + String currentHash = cryptoSensitiveHsmService.computeIntegrityHash( + record.getFieldList(), resolveValues(record.getFieldList(), er)); + if (currentHash.equals(record.getSm3Hash())) { + record.setVerifyResult(1); + passed++; + } else { + record.setVerifyResult(2); + tampered++; + log.error("执法登记完整性校验未通过, 疑似篡改, id={}, fieldList={}", + record.getBizId(), record.getFieldList()); + } + } + record.setLastVerifyTime(new Date()); + cryptoIntegrityDataMapper.updateById(record); + processed++; + } catch (Exception e) { + // 校验异常(如 fieldList 声明不支持字段): 状态 4, 与篡改(2)区分 + log.error("执法登记完整性校验异常, id={}", record.getBizId(), e); + record.setVerifyResult(4); + record.setLastVerifyTime(new Date()); + cryptoIntegrityDataMapper.updateById(record); + errored++; + processed++; + } + } + if (!result.hasNext()) { + break; + } + page.setCurrent(page.getCurrent() + 1); + } + if (tampered > 0) { + log.error("执法登记完整性校验发现疑似篡改记录, 数量={}(须人工核查, 基线已锁定拒绝自动覆盖)", tampered); + } + if (errored > 0) { + log.warn("执法登记完整性校验存在校验异常记录, 数量={}(程序缺陷或脏数据, 须排查代码, 非篡改)", errored); + } + log.info("执法登记完整性校验完成, 处理={}, 通过={}, 篡改={}, 校验异常={}, 跳过(数据不存在)={}", + processed, passed, tampered, errored, skipped); + return processed; + } + + /** + * 按基线 fieldList 声明解析当前库中字段值(顺序与 fieldList 严格对应) + */ + private String[] resolveValues(String fieldList, EnforcementRegistration er) { + String[] fields = fieldList.split(","); + String[] values = new String[fields.length]; + for (int i = 0; i < fields.length; i++) { + String field = fields[i].trim(); + switch (field) { + case "enterprise_id" -> values[i] = er.getEnterpriseId() != null ? er.getEnterpriseId() : ""; + case "law_enforcer_id" -> values[i] = er.getLawEnforcerId() != null ? er.getLawEnforcerId() : ""; + case "law_enforcement_item_text" -> values[i] = er.getLawEnforcementItemText() != null ? er.getLawEnforcementItemText() : ""; + default -> throw new ServiceException("不支持的执法登记完整性校验字段: " + field); + } + } + return values; + } +} diff --git a/zdxt-modules/zdxt-system/src/main/java/org/dromara/system/service/ICryptoSensitiveHsmService.java b/zdxt-modules/zdxt-system/src/main/java/org/dromara/system/service/ICryptoSensitiveHsmService.java index 8bf9f83f..6374e028 100644 --- a/zdxt-modules/zdxt-system/src/main/java/org/dromara/system/service/ICryptoSensitiveHsmService.java +++ b/zdxt-modules/zdxt-system/src/main/java/org/dromara/system/service/ICryptoSensitiveHsmService.java @@ -141,6 +141,16 @@ public interface ICryptoSensitiveHsmService { */ void saveIntegrityHash(String tableName, String bizId, String fieldList, String... plainValues); + /** + * 计算完整性摘要(HMAC-SM3, 新口径: fieldList + 定界符 + 各字段值整体参与哈希) + *

供跨模块校验场景重算摘要与基线比对; 口径与 saveIntegrityHash 完全一致

+ * + * @param fieldList 参与摘要的字段列表(逗号分隔, 与 crypto_integrity_data.field_list 一致) + * @param plainValues 各字段当前值(顺序与 fieldList 对应, 空值传 "") + * @return HMAC-SM3 摘要值; HSM 调用失败抛出异常 + */ + String computeIntegrityHash(String fieldList, String... plainValues); + /** * 异步保存数据完整性摘要 *

适用于操作日志等高频写入场景, 避免 HSM HMAC-SM3 远程调用阻塞调用线程; diff --git a/zdxt-modules/zdxt-system/src/main/java/org/dromara/system/service/impl/CryptoSensitiveHsmServiceImpl.java b/zdxt-modules/zdxt-system/src/main/java/org/dromara/system/service/impl/CryptoSensitiveHsmServiceImpl.java index ea2e6f7f..23e8f1da 100644 --- a/zdxt-modules/zdxt-system/src/main/java/org/dromara/system/service/impl/CryptoSensitiveHsmServiceImpl.java +++ b/zdxt-modules/zdxt-system/src/main/java/org/dromara/system/service/impl/CryptoSensitiveHsmServiceImpl.java @@ -313,7 +313,6 @@ public class CryptoSensitiveHsmServiceImpl implements ICryptoSensitiveHsmService * 计算并 upsert 完整性摘要(异常向上抛出,供需要失败统计的调用方使用) */ private void doSaveIntegrityHash(String tableName, String bizId, String fieldList, String... plainValues) { - // 新口径: fieldList 与字段定界均参与摘要计算(见 calculateHash) String hmacHash = calculateHash(fieldList, plainValues); LambdaQueryWrapper lqw = new LambdaQueryWrapper<>(); @@ -323,16 +322,14 @@ public class CryptoSensitiveHsmServiceImpl implements ICryptoSensitiveHsmService CryptoIntegrityData existing = cryptoIntegrityDataMapper.selectOne(lqw); if (existing != null) { - // 修复(防洗白): 已判定篡改(verifyResult=2)的基线禁止静默重置, 须人工核查后重建基线 if (existing.getVerifyResult() != null && existing.getVerifyResult() == 2) { - throw new ServiceException("完整性摘要基线已被标记篡改, 拒绝自动覆盖, 须人工核查: tableName=" + throw new ServiceException("完整性摘要基线已被标记篡改, 拒绝自动覆盖" + tableName + ", bizId=" + bizId); } existing.setFieldList(fieldList); existing.setSm3Hash(hmacHash); existing.setHashAlg("SM3"); existing.setGenerateTime(new Date()); - // 修复: 基线刷新后为"未校验"(0), 由校验任务确认后置 1; 不再生成即标通过, 且不再伪造 lastVerifyTime existing.setVerifyResult(0); cryptoIntegrityDataMapper.updateById(existing); } else { @@ -883,7 +880,6 @@ public class CryptoSensitiveHsmServiceImpl implements ICryptoSensitiveHsmService StringBuilder sb = new StringBuilder(); sb.append(fieldList != null ? fieldList.trim() : ""); for (String value : plainValues) { - // 单字节定界符, 值原样跟随, 消除拼接歧义 sb.append('\u0001'); sb.append(value != null ? value : ""); } @@ -1008,4 +1004,10 @@ public class CryptoSensitiveHsmServiceImpl implements ICryptoSensitiveHsmService default -> false; }; } + + @Override + public String computeIntegrityHash(String fieldList, String... plainValues) { + return calculateHash(fieldList, plainValues); + } + }