Browse Source

优化密评相关接口

dev-V1.0.9
vincent 4 weeks ago
parent
commit
694146a501
  1. 13
      zdxt-common/zdxt-common-encrypt/src/main/java/org/dromara/common/encrypt/utils/EncryptUtils.java
  2. 38
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/util/IdCardCryptoUtil.java
  3. 5
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/app/enforcement/AppEnforcementRegistrationController.java
  4. 3
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/app/enforcement/AppNonEnforcementRegistrationController.java
  5. 34
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/quartz/job/CryptoIntegrityHmackJob.java
  6. 29
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/quartz/job/CryptoSensitiveOperLogJob.java
  7. 30
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/quartz/job/CryptoSensitiveUserJob.java
  8. 2
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/quartz/job/SynDeptInfoJob.java
  9. 10
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/quartz/job/SynUserInfoJob.java
  10. 12
      zdxt-modules/zdxt-system/src/main/java/org/dromara/system/domain/bean/CryptoIntegrityData.java
  11. 13
      zdxt-modules/zdxt-system/src/main/java/org/dromara/system/mapper/SysOperLogMapper.java
  12. 11
      zdxt-modules/zdxt-system/src/main/java/org/dromara/system/mapper/SysUserMapper.java
  13. 36
      zdxt-modules/zdxt-system/src/main/java/org/dromara/system/service/ICryptoSensitiveHsmService.java
  14. 267
      zdxt-modules/zdxt-system/src/main/java/org/dromara/system/service/impl/CryptoSensitiveHsmServiceImpl.java
  15. 16
      zdxt-modules/zdxt-system/src/main/java/org/dromara/system/service/impl/SysOperLogServiceImpl.java
  16. 7
      zdxt-modules/zdxt-system/src/main/resources/mapper/system/SysOperLogMapper.xml
  17. 8
      zdxt-modules/zdxt-system/src/main/resources/mapper/system/SysUserMapper.xml

13
zdxt-common/zdxt-common-encrypt/src/main/java/org/dromara/common/encrypt/utils/EncryptUtils.java

@ -314,13 +314,16 @@ public class EncryptUtils {
public static void main(String[] args) {
KeyPair keyPair = SecureUtil.generateKeyPair("RSA");
byte[] publicKeyBytes = keyPair.getPublic().getEncoded();
byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();
EncryptUtils.decryptByAes("test", "zdxt2026PlatForm");
// KeyPair keyPair = SecureUtil.generateKeyPair("RSA");
//
// byte[] publicKeyBytes = keyPair.getPublic().getEncoded();
// byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();
String publicKeyBase64 = Base64.encode(publicKeyBytes);
String privateKeyBase64 = Base64.encode(privateKeyBytes);
//
// String publicKeyBase64 = Base64.encode(publicKeyBytes);
// String privateKeyBase64 = Base64.encode(privateKeyBytes);
// System.out.println("========== RSA 2048-bit 密钥对 ==========");
// System.out.println();

38
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/util/IdCardCryptoUtil.java

@ -26,11 +26,11 @@ public class IdCardCryptoUtil {
String key = "VRJHzFqJpsSZhwnsKTWNspv_GZMxTWl-bgH6DyE9Ty0";
System.out.println("====================== 示例 ======================");
// 加密
String source_dec = "15815555985";
String enc = IdCardCryptoUtil.symmetricEncryptIdCard(key, source_dec);
System.out.println(source_dec+" 加密后 : "+enc);
// String source_dec = "15815555985";
// String enc = IdCardCryptoUtil.symmetricEncryptIdCard(key, source_dec);
// System.out.println(source_dec+" 加密后 : "+enc);
// 解密
String source_enc = "wDLyWcBO/fIH2rJpSGN98A==";
String source_enc = "d2H344KpIlRusePxEJbgQg==";
String dec = IdCardCryptoUtil.symmetricDecryptIdCard(key, source_enc);
System.out.println(source_enc+" 解密后 : "+dec);
@ -42,16 +42,16 @@ public class IdCardCryptoUtil {
// System.out.println(phone);
// System.out.println(sfz);
System.out.println("====================== 实用 ======================");
String[] strings={
"13360957126",
"16688211945",
"17339838670",
"18460319603",
"15626498490",
"18080147218",
};
//
// System.out.println("====================== 实用 ======================");
// String[] strings={
// "13360957126",
// "16688211945",
// "17339838670",
// "18460319603",
// "15626498490",
// "18080147218",
// };
// 刘义 13410900924 19020722074
// 杜海荣 15019258592 19020722059
@ -60,11 +60,11 @@ public class IdCardCryptoUtil {
// 李大航 17799857610 19020722079
// String string="";
for(String s:strings){
String phoneJM=IdCardCryptoUtil.symmetricEncryptIdCard(key, s);
System.out.println(s+" 加密后: "+phoneJM);
// string=string+"'"+phoneJM+"',";
}
// for(String s:strings){
// String phoneJM=IdCardCryptoUtil.symmetricEncryptIdCard(key, s);
// System.out.println(s+" 加密后: "+phoneJM);
//// string=string+"'"+phoneJM+"',";
// }
// System.out.println(string);
/* String[] strings={

5
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/app/enforcement/AppEnforcementRegistrationController.java

@ -28,6 +28,8 @@ import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.helper.DataPermissionHelper;
@ -88,6 +90,7 @@ public class AppEnforcementRegistrationController extends BaseController {
@PostMapping("/add")
@ResponseBody
@RepeatSubmit()
@Log(title = "行政执法登记", businessType = BusinessType.INSERT)
public R<Void> addSave(@RequestBody EnforcementRegistrationBo bo)
{
if (StringUtils.isNotEmpty(bo.getId())) {
@ -198,6 +201,7 @@ public class AppEnforcementRegistrationController extends BaseController {
@PostMapping("/edit")
@ResponseBody
@RepeatSubmit()
@Log(title = "行政执法登记", businessType = BusinessType.UPDATE)
public R<Void> editSave(@RequestBody EnforcementRegistrationBo bo)
{
int resNum = 0;
@ -234,6 +238,7 @@ public class AppEnforcementRegistrationController extends BaseController {
@PostMapping("/editInfo")
@ResponseBody
@RepeatSubmit()
@Log(title = "行政执法登记", businessType = BusinessType.UPDATE)
public R<Void> editInfo(@RequestBody EnforcementRegistrationBo bo)
{
bo.setIsSubmit(0L);

3
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/app/enforcement/AppNonEnforcementRegistrationController.java

@ -5,6 +5,8 @@ import com.zdxt.enforcementcode.domain.bo.NonEnforcementRegistrationBo;
import com.zdxt.enforcementcode.domain.vo.NonEnforcementRegistrationVo;
import com.zdxt.enforcementcode.service.INonEnforcementRegistrationService;
import org.dromara.common.core.domain.R;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.web.core.BaseController;
@ -26,6 +28,7 @@ public class AppNonEnforcementRegistrationController extends BaseController {
*/
@PostMapping("/add")
@ResponseBody
@Log(title = "非行政执法登记", businessType = BusinessType.INSERT)
public R<Void> addSave(@RequestBody NonEnforcementRegistrationBo bo)
{

34
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/quartz/job/CryptoIntegrityHmackJob.java

@ -0,0 +1,34 @@
package com.zdxt.enforcementcode.quartz.job;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.system.service.ICryptoSensitiveHsmService;
import org.dromara.system.service.IPasswordPartyService;
import org.springframework.stereotype.Component;
/**
* 密码平台
* <p>通过 Quartz 定时触发,</p>
*
* @author zrb
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class CryptoIntegrityHmackJob {
private final ICryptoSensitiveHsmService cryptoSensitiveHsmService;
public void execute(){
this.hmac();
}
public void hmac(){
String [] tables = {"sys_user","sys_oper_log"};
for (int i = 0; i < tables.length; i++) {
log.info("数据完整性校验表开始{}", tables[i]);
cryptoSensitiveHsmService.verifyIntegrityHash(tables[i]);
log.info("数据完整性校验表结束{}", tables[i]);
}
}
}

29
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/quartz/job/CryptoSensitiveOperLogJob.java

@ -0,0 +1,29 @@
package com.zdxt.enforcementcode.quartz.job;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.system.service.ICryptoSensitiveHsmService;
import org.dromara.system.service.IPasswordPartyService;
import org.springframework.stereotype.Component;
/**
* 密码平台
* <p>通过 Quartz 定时触发,</p>
*
* @author zrb
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class CryptoSensitiveOperLogJob {
private final ICryptoSensitiveHsmService cryptoSensitiveHsmService;
public void execute(){
this.syncIntegrityHashModifiedOperLogs();
}
public void syncIntegrityHashModifiedOperLogs(){
log.info("同步操作日志密评开始");
cryptoSensitiveHsmService.syncIntegrityHashModifiedOperLogs();
log.info("同步操作日志密评结束");
}
}

30
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/quartz/job/CryptoSensitiveUserJob.java

@ -0,0 +1,30 @@
package com.zdxt.enforcementcode.quartz.job;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.system.service.ICryptoSensitiveHsmService;
import org.springframework.stereotype.Component;
/**
*
* <p>通过 Quartz 定时触发,</p>
*
* @author zrb
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class CryptoSensitiveUserJob {
private final ICryptoSensitiveHsmService cryptoSensitiveHsmService;
public void execute(){
this.syncIntegrityHashModifiedUsers();
}
public void syncIntegrityHashModifiedUsers(){
log.info("同步用户密评开始");
cryptoSensitiveHsmService.syncIntegrityHashModifiedUsers();
log.info("同步用户密评结束");
}
}

2
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/quartz/job/SynDeptInfoJob.java

@ -35,8 +35,6 @@ public class SynDeptInfoJob {
public static Map<String,String> params = new HashMap<>();
private static final Logger log = LoggerFactory.getLogger(SynDeptInfoJob.class);
public void run(){
this.deptInfo();

10
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/quartz/job/SynUserInfoJob.java

@ -36,7 +36,7 @@ public class SynUserInfoJob {
private ISysUserService sysUserService;
@Autowired
private ICryptoSensitiveHsmService ICryptoSensitiveHsmService;
private ICryptoSensitiveHsmService cryptoSensitiveHsmService;
public void run() {
this.userInfo();
@ -140,14 +140,14 @@ public class SynUserInfoJob {
// 密评规则:HSM 加密敏感字段存入 crypto_sensitive_data 表
String bizId = String.valueOf(userId);
try {
String phoneCipher = ICryptoSensitiveHsmService.encryptAndSave("sys_user", bizId, "phonenumber", mobileNumber);
String idCardCipher = ICryptoSensitiveHsmService.encryptAndSave("sys_user", bizId, "certificateNumber", certificateNumber);
String phoneCipher = cryptoSensitiveHsmService.encryptAndSave("sys_user", bizId, "phonenumber", mobileNumber);
String idCardCipher = cryptoSensitiveHsmService.encryptAndSave("sys_user", bizId, "certificateNumber", certificateNumber);
// 密码:仅新用户进行 HSM 加密,已有用户密码不动
String passwordCipher = null;
SysUserBo updateBo = new SysUserBo();
updateBo.setUserId(userId);
if (isNewUser) {
passwordCipher = ICryptoSensitiveHsmService.encryptAndSave("sys_user", bizId, "password", sysUserBo.getPassword());
passwordCipher = cryptoSensitiveHsmService.encryptAndSave("sys_user", bizId, "password", sysUserBo.getPassword());
updateBo.setPassword(passwordCipher);
}
updateBo.setPhonenumber(phoneCipher);
@ -164,7 +164,7 @@ public class SynUserInfoJob {
}
String phoneForHash = StringUtils.isNotEmpty(phoneCipher) ? phoneCipher : mobileNumber;
String idCardForHash = StringUtils.isNotEmpty(idCardCipher) ? idCardCipher : certificateNumber;
ICryptoSensitiveHsmService.saveIntegrityHash("sys_user", bizId,
cryptoSensitiveHsmService.saveIntegrityHash("sys_user", bizId,
"password,phonenumber,certificateNumber",
pwdForHash, phoneForHash, idCardForHash);
} catch (Exception e) {

12
zdxt-modules/zdxt-system/src/main/java/org/dromara/system/domain/bean/CryptoIntegrityData.java

@ -1,5 +1,7 @@
package org.dromara.system.domain.bean;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@ -70,4 +72,14 @@ public class CryptoIntegrityData {
* 逻辑删除
*/
private Integer isDeleted;
/**
* 创建时间
*/
private Date createTime;
/**
* 更新时间
*/
private Date updateTime;
}

13
zdxt-modules/zdxt-system/src/main/java/org/dromara/system/mapper/SysOperLogMapper.java

@ -1,9 +1,13 @@
package org.dromara.system.mapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Param;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.dromara.system.domain.SysOperLog;
import org.dromara.system.domain.vo.SysOperLogVo;
import java.util.Date;
/**
* 操作日志 数据层
*
@ -11,4 +15,13 @@ import org.dromara.system.domain.vo.SysOperLogVo;
*/
public interface SysOperLogMapper extends BaseMapperPlus<SysOperLog, SysOperLogVo> {
/**
* 分页查询指定操作时间范围内的操作日志记录(用于完整性摘要同步)
*
* @param page 分页参数
* @param beginTime 开始时间(含)
* @param endTime 结束时间(含)
* @return 操作日志分页记录
*/
Page<SysOperLog> selectByOperTimeRange(Page<SysOperLog> page, @Param("beginTime") Date beginTime, @Param("endTime") Date endTime);
}

11
zdxt-modules/zdxt-system/src/main/java/org/dromara/system/mapper/SysUserMapper.java

@ -17,6 +17,7 @@ import org.dromara.system.domain.bo.SysUserBo;
import org.dromara.system.domain.vo.SysUserExportVo;
import org.dromara.system.domain.vo.SysUserVo;
import java.util.Date;
import java.util.List;
/**
@ -156,4 +157,14 @@ public interface SysUserMapper extends BaseMapperPlus<SysUser, SysUserVo> {
List<SysUserVo> queryUserByIds(String[] ids);
Page<SysUserVo> queryUsersByPage(@Param("page") Page<SysUserVo> page, @Param("user") SysUserBo user);
/**
* 分页查询指定更新时间范围内的用户表记录(用于完整性摘要同步)
*
* @param page 分页参数
* @param beginTime 开始时间(含)
* @param endTime 结束时间(含)
* @return 用户分页记录
*/
Page<SysUser> selectModifiedUserByUpdateTime(Page<SysUser> page, @Param("beginTime") Date beginTime, @Param("endTime") Date endTime);
}

36
zdxt-modules/zdxt-system/src/main/java/org/dromara/system/service/ICryptoSensitiveHsmService.java

@ -64,4 +64,40 @@ public interface ICryptoSensitiveHsmService {
* @param plainValues 与 fieldList 顺序一致的明文值
*/
void saveIntegrityHash(String tableName, String bizId, String fieldList, String... plainValues);
/**
* 同步前一天修改过的 sys_user 数据完整性摘要
* <p>
* 查询 sys_user 表中 update_time 落在前一天范围内的记录,
* 以数据库中实际存储值(password、phonenumber、certificateNumber)重新计算 HMAC-SM3 摘要,
* 按 upsert 策略写入 crypto_integrity_data 表,供后续完整性校验比对。
*
* @return 成功处理的记录数
*/
int syncIntegrityHashModifiedUsers();
/**
* 同步前一天产生的 sys_oper_log 数据完整性摘要
* <p>
* 查询 sys_oper_log 表中 oper_time 落在前一天范围内的记录,
* 以数据库中实际存储值(business_type、method、oper_name、oper_param、oper_time)
* 重新计算 HMAC-SM3 摘要,按 upsert 策略写入 crypto_integrity_data 表,供后续完整性校验比对。
*
* @return 成功处理的记录数
*/
int syncIntegrityHashModifiedOperLogs();
/**
* 校验指定业务表的数据完整性摘要(HMAC-SM3)
* <p>
* 查询 crypto_integrity_data 中 table_name 匹配且 generate_time 早于今天的基准摘要记录,
* 根据 table_name 与 biz_id 回查业务表当前数据重新计算摘要并比对:
* 一致则标记 verify_result=1(通过),不一致或业务数据缺失则标记 verify_result=2(篡改),
* 同时刷新 last_verify_time。
*
* @param tableName 业务表名,当前支持 sys_user、sys_oper_log
* @return 完成校验的记录数
*/
int verifyIntegrityHash(String tableName);
}

267
zdxt-modules/zdxt-system/src/main/java/org/dromara/system/service/impl/CryptoSensitiveHsmServiceImpl.java

@ -1,12 +1,18 @@
package org.dromara.system.service.impl;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.mybatis.helper.DataPermissionHelper;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.system.domain.SysOperLog;
import org.dromara.system.domain.SysUser;
import org.dromara.system.domain.bean.CryptoIntegrityData;
import org.dromara.system.domain.bean.CryptoOperationAudit;
import org.dromara.system.domain.bean.CryptoSensitiveData;
@ -14,8 +20,11 @@ import org.dromara.system.domain.vo.SymmetryEncryptVo;
import org.dromara.system.mapper.CryptoIntegrityDataMapper;
import org.dromara.system.mapper.CryptoOperationAuditMapper;
import org.dromara.system.mapper.CryptoSensitiveDataMapper;
import org.dromara.system.mapper.SysOperLogMapper;
import org.dromara.system.mapper.SysUserMapper;
import org.dromara.system.service.ICryptoSensitiveHsmService;
import org.dromara.system.service.IPasswordPartyService;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@ -31,9 +40,12 @@ public class CryptoSensitiveHsmServiceImpl implements ICryptoSensitiveHsmService
private final CryptoSensitiveDataMapper cryptoSensitiveDataMapper;
private final CryptoOperationAuditMapper cryptoOperationAuditMapper;
private final CryptoIntegrityDataMapper cryptoIntegrityDataMapper;
private final SysUserMapper sysUserMapper;
private final SysOperLogMapper sysOperLogMapper;
private static final String KEY_VERSION = "V1";
private static final String TABLE_SYS_USER = "sys_user";
private static final String TABLE_SYS_OPER_LOG = "sys_oper_log";
@Override
@Transactional(rollbackFor = Exception.class)
@ -111,40 +123,152 @@ public class CryptoSensitiveHsmServiceImpl implements ICryptoSensitiveHsmService
@Override
@Transactional(rollbackFor = Exception.class)
public void saveIntegrityHash(String tableName, String bizId, String fieldList, String... plainValues) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < plainValues.length; i++) {
if (i > 0) sb.append(",");
sb.append(plainValues[i] != null ? plainValues[i] : "");
try{
String hmacHash = calculateHash(fieldList, plainValues);
LambdaQueryWrapper<CryptoIntegrityData> lqw = new LambdaQueryWrapper<>();
lqw.eq(CryptoIntegrityData::getTableName, tableName)
.eq(CryptoIntegrityData::getBizId, bizId)
.eq(CryptoIntegrityData::getIsDeleted, 0);
CryptoIntegrityData existing = cryptoIntegrityDataMapper.selectOne(lqw);
if (existing != null) {
existing.setFieldList(fieldList);
existing.setSm3Hash(hmacHash);
existing.setHashAlg("SM3");
Date date = new Date();
existing.setGenerateTime(date);
existing.setLastVerifyTime(date);
existing.setVerifyResult(1);
cryptoIntegrityDataMapper.updateById(existing);
} else {
CryptoIntegrityData record = new CryptoIntegrityData();
record.setTableName(tableName);
record.setBizId(bizId);
record.setFieldList(fieldList);
record.setSm3Hash(hmacHash);
record.setHashAlg("SM3");
Date date = new Date();
record.setGenerateTime(date);
record.setLastVerifyTime(date);
record.setVerifyResult(1);
record.setIsDeleted(0);
cryptoIntegrityDataMapper.insert(record);
}
} catch (Exception e) {
log.error("保存数据完整性摘要失败, tableName={}, bizId={}", tableName, bizId, e);
}
String concatPlain = sb.toString();
String inputHex = HexUtil.encodeHexStr(concatPlain.getBytes(StandardCharsets.UTF_8));
String hmacHash = passwordPartyService.hmacWithIndex(inputHex);
}
LambdaQueryWrapper<CryptoIntegrityData> lqw = new LambdaQueryWrapper<>();
lqw.eq(CryptoIntegrityData::getTableName, tableName)
.eq(CryptoIntegrityData::getBizId, bizId)
.eq(CryptoIntegrityData::getIsDeleted, 0);
CryptoIntegrityData existing = cryptoIntegrityDataMapper.selectOne(lqw);
@Override
public int verifyIntegrityHash(String tableName) {
if (!TABLE_SYS_USER.equals(tableName) && !TABLE_SYS_OPER_LOG.equals(tableName)) {
throw new ServiceException("暂不支持该业务表的完整性校验: " + tableName);
}
Date today = DateUtil.beginOfDay(new Date());
if (existing != null) {
existing.setFieldList(fieldList);
existing.setSm3Hash(hmacHash);
existing.setHashAlg("SM3");
existing.setGenerateTime(new Date());
existing.setVerifyResult(0);
cryptoIntegrityDataMapper.updateById(existing);
} else {
CryptoIntegrityData record = new CryptoIntegrityData();
record.setTableName(tableName);
record.setBizId(bizId);
record.setFieldList(fieldList);
record.setSm3Hash(hmacHash);
record.setHashAlg("SM3");
record.setGenerateTime(new Date());
record.setVerifyResult(0);
record.setIsDeleted(0);
cryptoIntegrityDataMapper.insert(record);
int processed = 0;
int passed = 0;
int tampered = 0;
long pageSize = 500L;
Page<CryptoIntegrityData> page = new Page<>(1, pageSize);
LambdaQueryWrapper<CryptoIntegrityData> lqw = new LambdaQueryWrapper<CryptoIntegrityData>()
.eq(CryptoIntegrityData::getTableName, tableName)
.eq(CryptoIntegrityData::getIsDeleted, 0)
.lt(CryptoIntegrityData::getGenerateTime, today);
while (true) {
Page<CryptoIntegrityData> result = cryptoIntegrityDataMapper.selectPage(page, lqw);
for (CryptoIntegrityData record : result.getRecords()) {
try {
boolean matched = TABLE_SYS_USER.equals(tableName)
? verifySysUserIntegrity(record)
: verifySysOperLogIntegrity(record);
record.setVerifyResult(matched ? 1 : 2);
record.setLastVerifyTime(new Date());
cryptoIntegrityDataMapper.updateById(record);
processed++;
if (matched) {
passed++;
} else {
tampered++;
}
} catch (Exception e) {
log.warn("完整性校验失败, tableName={}, bizId={}", record.getTableName(), record.getBizId(), e);
}
}
if (!result.hasNext()) {
break;
}
page.setCurrent(page.getCurrent() + 1);
}
log.info("完整性校验完成, tableName={}, 处理={}, 通过={}, 异常={}", tableName, processed, passed, tampered);
return processed;
}
@Override
public int syncIntegrityHashModifiedUsers() {
Date begin = DateUtil.beginOfDay(DateUtil.yesterday());
Date end = DateUtil.endOfDay(DateUtil.yesterday());
int processed = 0;
long pageSize = 500L;
Page<SysUser> page = new Page<>(1, pageSize);
while (true) {
Page<SysUser> result = DataPermissionHelper.ignore(
() -> sysUserMapper.selectModifiedUserByUpdateTime(page, begin, end));
for (SysUser user : result.getRecords()) {
try {
saveIntegrityHash(TABLE_SYS_USER, String.valueOf(user.getUserId()),
"password,phonenumber,certificateNumber",
user.getPassword() != null ? user.getPassword() : "",
user.getPhonenumber() != null ? user.getPhonenumber() : "",
user.getCertificateNumber() != null ? user.getCertificateNumber() : "");
processed++;
} catch (Exception e) {
log.warn("同步前一天修改用户完整性摘要失败, userId={}", user.getUserId(), e);
}
}
if (!result.hasNext()) {
break;
}
page.setCurrent(page.getCurrent() + 1);
}
log.info("同步前一天修改用户完整性摘要完成, 处理数量={}", processed);
return processed;
}
@Override
public int syncIntegrityHashModifiedOperLogs() {
Date begin = DateUtil.beginOfDay(DateUtil.yesterday());
Date end = DateUtil.endOfDay(DateUtil.yesterday());
int processed = 0;
long pageSize = 500L;
Page<SysOperLog> page = new Page<>(1, pageSize);
while (true) {
Page<SysOperLog> result = DataPermissionHelper.ignore(
() -> sysOperLogMapper.selectByOperTimeRange(page, begin, end));
for (SysOperLog operLog : result.getRecords()) {
try {
saveIntegrityHash(TABLE_SYS_OPER_LOG, String.valueOf(operLog.getOperId()),
"business_type,method,oper_name,oper_param,oper_time",
toStr(operLog.getBusinessType()),
toStr(operLog.getMethod()),
toStr(operLog.getOperName()),
toStr(operLog.getOperParam()),
toStr(operLog.getOperTime()));
processed++;
} catch (Exception e) {
log.warn("同步前一天操作日志完整性摘要失败, operId={}", operLog.getOperId(), e);
}
}
if (!result.hasNext()) {
break;
}
page.setCurrent(page.getCurrent() + 1);
}
log.info("同步前一天操作日志完整性摘要完成, 处理数量={}", processed);
return processed;
}
private CryptoSensitiveData queryActiveRecord(String tableName, String bizId, String fieldCode) {
@ -183,6 +307,16 @@ public class CryptoSensitiveHsmServiceImpl implements ICryptoSensitiveHsmService
cryptoOperationAuditMapper.insert(audit);
}
private String calculateHash(String fieldList, String... plainValues) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < plainValues.length; i++) {
if (i > 0) sb.append(",");
sb.append(plainValues[i] != null ? plainValues[i] : "");
}
String inputHex = HexUtil.encodeHexStr(sb.toString().getBytes(StandardCharsets.UTF_8));
return passwordPartyService.hmacWithIndex(inputHex);
}
private static byte[] hexToBytes(String hex) {
int len = hex.length();
byte[] data = new byte[len / 2];
@ -192,4 +326,77 @@ public class CryptoSensitiveHsmServiceImpl implements ICryptoSensitiveHsmService
}
return data;
}
/**
* 校验 sys_user 表数据完整性
* <p>
* 根据基准摘要记录的 biz_id 回查 sys_user 当前数据,
* 按 field_list 解析参与字段(password、phonenumber、certificateNumber),
* 重新计算 HMAC-SM3 摘要并与基准 sm3_hash 比对。
*
* @param record 基准摘要记录
* @return true=校验通过,false=数据缺失或摘要不一致(疑似篡改)
*/
private boolean verifySysUserIntegrity(CryptoIntegrityData record) {
SysUser user = DataPermissionHelper.ignore(
() -> sysUserMapper.selectById(Long.valueOf(record.getBizId())));
if (user == null) {
log.warn("完整性校验: sys_user 数据不存在, bizId={}", record.getBizId());
return false;
}
String[] fields = record.getFieldList().split(",");
String[] values = new String[fields.length];
for (int i = 0; i < fields.length; i++) {
String field = fields[i].trim();
switch (field) {
case "password" -> values[i] = user.getPassword() != null ? user.getPassword() : "";
case "phonenumber" -> values[i] = user.getPhonenumber() != null ? user.getPhonenumber() : "";
case "certificateNumber" -> values[i] = user.getCertificateNumber() != null ? user.getCertificateNumber() : "";
default -> throw new ServiceException("不支持的完整性校验字段: " + field);
}
}
String currentHash = calculateHash(record.getFieldList(), values);
boolean matched = currentHash.equals(record.getSm3Hash());
if (!matched) {
log.warn("数据完整性校验未通过, 疑似篡改, tableName={}, bizId={}", record.getTableName(), record.getBizId());
}
return matched;
}
/**
* 校验 sys_oper_log 表数据完整性
* <p>
* 根据基准摘要记录的 biz_id 回查 sys_oper_log 当前数据,
* 固定按 business_type、method、oper_name、oper_param、oper_time 五个字段
* 重新计算 HMAC-SM3 摘要并与基准 sm3_hash 比对。
* 非字符串字段统一按 String.valueOf 转换,与保存摘要时的取值口径保持一致。
*
* @param record 基准摘要记录
* @return true=校验通过,false=数据缺失或摘要不一致(疑似篡改)
*/
private boolean verifySysOperLogIntegrity(CryptoIntegrityData record) {
SysOperLog operLog = DataPermissionHelper.ignore(
() -> sysOperLogMapper.selectById(Long.valueOf(record.getBizId())));
if (operLog == null) {
log.warn("完整性校验: sys_oper_log 数据不存在, bizId={}", record.getBizId());
return false;
}
String[] values = new String[]{
toStr(operLog.getBusinessType()),
toStr(operLog.getMethod()),
toStr(operLog.getOperName()),
toStr(operLog.getOperParam()),
toStr(operLog.getOperTime())
};
String currentHash = calculateHash(record.getFieldList(), values);
boolean matched = currentHash.equals(record.getSm3Hash());
if (!matched) {
log.warn("数据完整性校验未通过, 疑似篡改, tableName={}, bizId={}", record.getTableName(), record.getBizId());
}
return matched;
}
private static String toStr(Object value) {
return value != null ? String.valueOf(value) : "";
}
}

16
zdxt-modules/zdxt-system/src/main/java/org/dromara/system/service/impl/SysOperLogServiceImpl.java

@ -4,6 +4,7 @@ import cn.hutool.core.util.ArrayUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.core.utils.ip.AddressUtils;
@ -14,6 +15,7 @@ import org.dromara.system.domain.SysOperLog;
import org.dromara.system.domain.bo.SysOperLogBo;
import org.dromara.system.domain.vo.SysOperLogVo;
import org.dromara.system.mapper.SysOperLogMapper;
import org.dromara.system.service.ICryptoSensitiveHsmService;
import org.dromara.system.service.ISysOperLogService;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
@ -29,11 +31,13 @@ import java.util.Map;
*
* @author Lion Li
*/
@Slf4j
@RequiredArgsConstructor
@Service
public class SysOperLogServiceImpl implements ISysOperLogService {
private final SysOperLogMapper baseMapper;
private final ICryptoSensitiveHsmService cryptoSensitiveHsmService;
/**
* 操作日志记录
@ -95,6 +99,18 @@ public class SysOperLogServiceImpl implements ISysOperLogService {
SysOperLog operLog = MapstructUtils.convert(bo, SysOperLog.class);
operLog.setOperTime(new Date());
baseMapper.insert(operLog);
try {
cryptoSensitiveHsmService.saveIntegrityHash("sys_oper_log", String.valueOf(operLog.getOperId()),
"business_type,method,oper_name,oper_param,oper_time",
String.valueOf(operLog.getBusinessType()),
operLog.getMethod() != null ? operLog.getMethod() : "",
operLog.getOperName() != null ? operLog.getOperName() : "",
operLog.getOperParam() != null ? operLog.getOperParam() : "",
String.valueOf(operLog.getOperTime()));
} catch (Exception e) {
log.warn("操作日志完整性摘要保存失败, operId={}", operLog.getOperId(), e);
}
}
/**

7
zdxt-modules/zdxt-system/src/main/resources/mapper/system/SysOperLogMapper.xml

@ -4,4 +4,11 @@
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.system.mapper.SysOperLogMapper">
<select id="selectByOperTimeRange" resultType="org.dromara.system.domain.SysOperLog">
select oper_id, business_type, method, oper_name, oper_param, oper_time
from sys_oper_log
where oper_time &gt;= #{beginTime}
and oper_time &lt;= #{endTime}
</select>
</mapper>

8
zdxt-modules/zdxt-system/src/main/resources/mapper/system/SysUserMapper.xml

@ -237,4 +237,12 @@
#{id}
</foreach>
</select>
<select id="selectModifiedUserByUpdateTime" resultType="org.dromara.system.domain.SysUser">
select u.user_id, u.password, u.phonenumber, u.certificateNumber
from sys_user u
where u.del_flag = '0'
and u.update_time &gt;= #{beginTime}
and u.update_time &lt;= #{endTime}
</select>
</mapper>

Loading…
Cancel
Save