修改日志签名接口

This commit is contained in:
xydkj 2026-04-18 14:35:49 +08:00
parent 350fa4d89d
commit ee710e9b00
9 changed files with 359 additions and 40 deletions

View File

@ -28,6 +28,7 @@ public class IpWhitelistController {
@PostMapping("/add")
@Operation(summary = "添加IP白名单", description = "新增一条IP白名单记录")
@ReplayProtected
@AuditedOperation(module = ModuleCode.DEVICE, action = ActionType.CREATE, summary = "添加IP白名单")
public ApiResponse<Void> addWhitelist(@RequestBody IpWhitelistRequest req) {
ipWhitelistService.addWhitelist(req);
return ApiResponse.success();

View File

@ -8,8 +8,8 @@ import lombok.Data;
@Schema(description = "日志审计请求参数")
public class AuditLogReviewRequest {
@Schema(description = "审计管理员签名值", example = "MEUC...")
private String auditSign;
// @Schema(description = "审计管理员签名值", example = "MEUC...")
// private String auditSign;
@Schema(description = "审计结果 (PASS/ REJECT / NEED_VERIFY)", example = "PASS")
private AuditResult auditResult;
@Schema(description = "审计意见", example = "审核通过")

View File

@ -11,6 +11,6 @@ public interface OperationAuditLogRepository {
void add(OperationAuditLogEntity auditLogEntity);
IPage<OperationAuditLogEntity> findPage(OperationAuditLogPageRequest req);
Optional<OperationAuditLogEntity> findByLogId(String logId);
void update(OperationAuditLogEntity entity);
Optional<List<OperationAuditLogEntity>> findByAuditStatus (String auditStatus);
void updateByLogId(OperationAuditLogEntity entity);
Optional<List<OperationAuditLogEntity>> findAll ();
}

View File

@ -1,7 +1,6 @@
package com.cisd.tms.modules.log.repository.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@ -70,16 +69,17 @@ public class OperationAuditLogRepositoryImpl implements OperationAuditLogReposit
}
@Override
public void update(OperationAuditLogEntity entity) {
operationAuditLogMapper.updateById(entity);
public void updateByLogId(OperationAuditLogEntity entity) {
LambdaQueryWrapper<OperationAuditLogEntity> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(OperationAuditLogEntity::getLogId, entity.getLogId());
operationAuditLogMapper.update(entity, wrapper);
}
@Override
public Optional<List<OperationAuditLogEntity>> findByAuditStatus(String auditStatus) {
QueryWrapper<OperationAuditLogEntity> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("audit_status", auditStatus);
List<OperationAuditLogEntity> logList = operationAuditLogMapper.selectList(queryWrapper);
public Optional<List<OperationAuditLogEntity>> findAll() {
LambdaQueryWrapper<OperationAuditLogEntity> wrapper = new LambdaQueryWrapper<>();
List<OperationAuditLogEntity> logList = operationAuditLogMapper.selectList(wrapper);
return Optional.ofNullable(logList);
}
}

View File

@ -0,0 +1,64 @@
//package com.cisd.tms.modules.log.runner;
//
//import com.cisd.tms.modules.log.dto.BackupConfigUpdateRequest;
//import com.cisd.tms.modules.log.entity.BackupConfigEntity;
//import com.cisd.tms.modules.log.repository.BackupConfigRepository;
//import com.cisd.tms.modules.log.service.AuditBackupService;
//import org.slf4j.Logger;
//import org.slf4j.LoggerFactory;
//import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.boot.CommandLineRunner;
//import org.springframework.stereotype.Component;
//
//import java.util.ArrayList;
//import java.util.List;
//
//
//@Component
//public class BackupTaskStartupRunner implements CommandLineRunner {
//
// private static final Logger log = LoggerFactory.getLogger(BackupTaskStartupRunner.class);
//
// @Autowired
// private BackupConfigRepository backupConfigRepository;
//
// @Autowired
// private AuditBackupService auditBackupService;
//
// @Override
// public void run(String... args) throws Exception {
// log.info("============= 系统启动:开始初始化审计日志备份定时任务 =============");
//
// try {
// List<BackupConfigEntity> configList = backupConfigRepository.selectAll().orElseGet(ArrayList::new);
//
// if (configList.isEmpty()) {
// log.info("未在数据库中找到备份策略配置,跳过任务初始化。");
// return;
// }
//
// for (BackupConfigEntity config : configList) {
// // 仅当状态为启用 (1) cron 表达式不为空时才去开启任务
// if (config.getEnable() != null && config.getEnable() == 1
// && config.getCronExp() != null && !config.getCronExp().isEmpty()) {
//
// BackupConfigUpdateRequest req = new BackupConfigUpdateRequest();
// req.setCronExp(config.getCronExp());
// req.setEnable(config.getEnable());
// req.setRetentionCount(config.getRetentionCount());
// auditBackupService.updateAndRestartBackupTask(
// req
// );
// log.info("成功加载并开启备份任务配置ID: {}, Cron: {}", config.getId(), config.getCronExp());
// } else {
// log.info("配置ID: {} 未启用或 Cron 为空,跳过。", config.getId());
// }
// }
//
// log.info("============= 审计日志备份定时任务初始化完成 =============");
//
// } catch (Exception e) {
// log.error("初始化审计日志备份任务发生异常: {}", e.getMessage(), e);
// }
// }
//}

View File

@ -0,0 +1,248 @@
//package com.cisd.tms.modules.log.service;
//
//import com.cisd.tms.integration.crypto.pcie.model.BackupDataResult;
//import com.cisd.tms.integration.crypto.pcie.model.EccInternalEncryptRequest;
//import com.cisd.tms.integration.crypto.pcie.service.PcieCryptoService;
//import com.cisd.tms.modules.log.dto.BackupConfigRequest;
//import com.cisd.tms.modules.log.entity.BackupConfigEntity;
//import com.cisd.tms.modules.log.entity.BackupRecordEntity;
//import com.cisd.tms.modules.log.entity.OperationAuditLogEntity;
//import com.cisd.tms.modules.log.enums.ActionType;
//import com.cisd.tms.modules.log.enums.OperationResult;
//import com.cisd.tms.modules.log.repository.BackupConfigRepository;
//import com.cisd.tms.modules.log.repository.BackupRecordRepository;
//import com.cisd.tms.modules.log.repository.OperationAuditLogRepository;
//import org.slf4j.Logger;
//import org.slf4j.LoggerFactory;
//import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
//import org.springframework.scheduling.support.CronExpression;
//import org.springframework.scheduling.support.CronTrigger;
//import org.springframework.stereotype.Service;
//import org.springframework.transaction.annotation.Transactional;
//
//import javax.crypto.Cipher;
//import javax.crypto.spec.IvParameterSpec;
//import javax.crypto.spec.SecretKeySpec;
//import java.io.File;
//import java.io.FileOutputStream;
//import java.io.IOException;
//import java.nio.charset.StandardCharsets;
//import java.text.SimpleDateFormat;
//import java.time.LocalDateTime;
//import java.time.format.DateTimeFormatter;
//import java.util.*;
//import java.util.concurrent.ScheduledFuture;
//import java.util.zip.ZipEntry;
//import java.util.zip.ZipOutputStream;
//
//@Service
//public class AuditBackupService {
//
// private static final Logger log = LoggerFactory.getLogger(AuditBackupService.class);
//
// @Autowired
// private BackupConfigRepository backupConfigRepository;
//
// @Autowired
// private ThreadPoolTaskScheduler taskScheduler;
//
// @Autowired
// private OperationAuditLogRepository operationAuditLogRepository;
//
// @Autowired
// private BackupRecordRepository backupRecordRepository;
//
// @Autowired
// private PcieCryptoService pcieCryptoService;
// private ScheduledFuture<?> currentTaskFuture;
// private static final String PROVIDER = "BC";
//
// @Transactional(rollbackFor = Exception.class)
// public void updateAndRestartBackupTask(BackupConfigRequest req) {
// String cronExp = trim(req.getCronExp());
// if (!CronExpression.isValidExpression(req.getCronExp())) {
// throw new IllegalArgumentException("Cron表达式格式不正确请重新输入");
// }
//
// BackupConfigEntity updateEntity = new BackupConfigEntity();
// updateEntity.setEnable(req.getEnable());
// updateEntity.setCronExp(req.getCronExp());
// if (req.getRetentionCount() != null) {
// updateEntity.setRetentionCount(req.getRetentionCount());
// }
//
// backupConfigRepository.update(updateEntity);
//
//
// if (currentTaskFuture != null && !currentTaskFuture.isCancelled()) {
// currentTaskFuture.cancel(true);
// log.info("已关闭旧的备份定时任务");
// }
//
//
// if (req.getEnable() == 1) {
// currentTaskFuture = taskScheduler.schedule(
// () -> executeBackup(), // 指向下面的备份方法
// new CronTrigger(cronExp)
// );
// log.info("已开启新的备份定时任务Cron表达式: {}", cronExp);
// }
// }
//
// private void executeBackup() {
// log.info("开始执行审计日志定期备份任务");
//
// String timeStr = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss"));
// // 定义压缩包的名称和路径
// String zipFileName = "audit_backup_" + timeStr + ".zip";
// String zipFilePath = "./data/backup/" + zipFileName;
//
// File zipFile = new File(zipFilePath);
// zipFile.getParentFile().mkdirs(); // 确保目录存在
//
// boolean isBackupSuccess = false;
// try (FileOutputStream fos = new FileOutputStream(zipFile);
// ZipOutputStream zos = new ZipOutputStream(fos)) {
//
// StringBuilder csvContent = new StringBuilder();
//
// csvContent.append("日志ID,发生时间,操作角色,认证等级,业务模块,操作动作,操作结果,来源IP,操作摘要,失败原因,审计状态,审计结果,审计人,审计时间,防篡改摘要(SM3),签名值(SM2)\n");
//
// List<OperationAuditLogEntity> logList = operationAuditLogRepository.findAll().orElseGet(ArrayList::new);
// SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//
// for (OperationAuditLogEntity auditLog : logList) {
// String summary = escapeCSV(auditLog.getSummary());
// String errorMsg = escapeCSV(auditLog.getErrorMessage());
// ActionType actionType = auditLog.getActionType();
// OperationResult result = auditLog.getOperationResult();
//
// String occurredTime = auditLog.getOccurredAt() != null ? sdf.format(auditLog.getOccurredAt()) : "";
// String auditedTime = auditLog.getAuditedAt() != null ? sdf.format(auditLog.getAuditedAt()) : "";
//
// String line = String.format("%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n",
// auditLog.getLogId(),
// occurredTime,
// auditLog.getOperatorRoleCode(),
// auditLog.getOperatorAuthLevel(),
// auditLog.getModuleCode(),
// actionType,
// result,
// auditLog.getRemoteIp(),
// summary,
// errorMsg,
// auditLog.getAuditStatus(),
// auditLog.getAuditResult(),
// auditLog.getAuditedBy(),
// auditedTime,
// auditLog.getPayloadHash(),
// auditLog.getSignValue()
// );
// csvContent.append(line);
// }
//
// byte[] orgDataBytes = csvContent.toString().getBytes(StandardCharsets.UTF_8);
//
// byte[] iv = randomBytes(16);
// byte[] sm4Key = randomBytes(16);
//
// Cipher cipher = Cipher.getInstance("SM4/CBC/PKCS7Padding", PROVIDER);
// cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(sm4Key, "SM4"), new IvParameterSpec(iv));
//
// EccInternalEncryptRequest encryptRequest = new EccInternalEncryptRequest();
// encryptRequest.setKeyIndex(1);
// encryptRequest.setData(sm4Key);
// BackupDataResult wrappedDek = pcieCryptoService.sdfeInternalEncryptEcc(encryptRequest);
//
// Map<String, Object> envelope = new LinkedHashMap<>();
// envelope.put("version", 1);
// envelope.put("payloadAlg", "SM4-CBC");
// envelope.put("keyWrapAlg", "SDFE_InternalEncrypt_ECC");
// envelope.put("ivBase64", Base64.getEncoder().encodeToString(iv));
// envelope.put("wrappedDekBase64", Base64.getEncoder().encodeToString(normalize(wrappedDek)));
// envelope.put("dekLength", sm4Key.length);
//
//
// byte[] encryptedData = cipher.doFinal(orgDataBytes);
//
// ZipEntry csvEntry = new ZipEntry("data.csv.enc");
// zos.putNextEntry(csvEntry);
//
// zos.write(encryptedData);
// zos.closeEntry();
//
// log.info("审计日志备份完成,共备份 {} 条", logList.size());
//
// // 更新备份更新时间
// BackupConfigEntity config = new BackupConfigEntity();
// config.setLastBackupTime(LocalDateTime.now());
// backupConfigRepository.saveOrUpdate(config);
//
// isBackupSuccess = true;
//
// } catch (IOException e) {
// log.error("写入 ZIP 压缩包发生异常: {}", e.getMessage(), e);
// } catch (Exception e) {
// log.error("执行备份任务发生异常: {}", e.getMessage(), e);
// }
//
//
// if (isBackupSuccess) {
// try {
// BackupRecordEntity record = new BackupRecordEntity()
// .setFileName(zipFileName)
// .setFilePath(zipFilePath)
// .setStatus("SUCCESS");
//
// backupRecordRepository.add(record);
//
// log.info("备份记录已成功存入 tms_log_backup 表。");
// } catch (Exception e) {
// log.error("保存备份记录到数据库失败: {}", e.getMessage(), e);
// }
// }
// }
//
// private String escapeCSV(String str) {
// if (str == null) {
// return "";
// }
//
// boolean containSpecialChar = str.contains(",")
// || str.contains("\"")
// || str.contains("\n")
// || str.contains("\r");
//
// if (containSpecialChar) {
// str = str.replace("\"", "\"\"");
// str = "\"" + str + "\"";
// }
//
// return str;
// }
//
//
// private static byte[] normalize(BackupDataResult result) {
// if (result == null || result.getData() == null) {
// return new byte[0];
// }
// int safeLength = result.getLength() > 0 && result.getLength() <= result.getData().length
// ? result.getLength()
// : result.getData().length;
// byte[] normalized = new byte[safeLength];
// System.arraycopy(result.getData(), 0, normalized, 0, safeLength);
// return normalized;
// }
//
//
// private static byte[] randomBytes(int size) {
// byte[] bytes = new byte[size];
// new java.security.SecureRandom().nextBytes(bytes);
// return bytes;
// }
//
// private String trim(String str) {
// return str == null ? "" : str.trim();
// }
//}

View File

@ -3,7 +3,6 @@ package com.cisd.tms.modules.log.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.cisd.tms.common.enums.ErrorCode;
import com.cisd.tms.common.exception.BizException;
import com.cisd.tms.integration.crypto.pcie.model.EccInternalVerifyRequest;
import com.cisd.tms.integration.crypto.pcie.service.PcieCryptoService;
import com.cisd.tms.modules.log.annotation.AuditedOperation;
import com.cisd.tms.modules.log.dto.*;
@ -21,7 +20,6 @@ import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.method.HandlerMethod;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.Base64;
import java.util.UUID;
@ -137,6 +135,7 @@ public class OperationAuditService {
resp.setAuditedBy(entity.getAuditedBy());
resp.setRemoteIp(entity.getRemoteIp());
resp.setErrorMessage(entity.getErrorMessage());
resp.setSignValue(entity.getSignValue());
resp.setOccurredAt(entity.getOccurredAt());
return resp;
});
@ -169,24 +168,32 @@ public class OperationAuditService {
OperationAuditLogEntity existLog = operationAuditLogRepository.findByLogId(logId)
.orElseThrow(() -> new BizException(ErrorCode.VALIDATE_FAILED.getCode(), "未找到该logId"));
String signValue = trim(req.getAuditSign());
if (signValue.isEmpty()){
throw new IllegalArgumentException("签名值不能为空");
}
//todo 验签
EccInternalVerifyRequest VerifyRequest = new EccInternalVerifyRequest();
// todo后续更改
VerifyRequest.setKeyIndex(1);
VerifyRequest.setData(req.getAuditSign().getBytes(StandardCharsets.UTF_8));
VerifyRequest.setSignature(decodeBlob(signValue, "signature is invalid"));
try {
pcieCryptoService.eccInternalVerify(VerifyRequest);
} catch (BizException ex) {
throw ex;
} catch (RuntimeException ex) {
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "signature verification failed");
}
// String signValue = trim(req.getAuditSign());
// if (signValue.isEmpty()){
// throw new IllegalArgumentException("签名值不能为空");
// }
//
// String payload = auditSigner.buildAuditPayload(existLog);
//
// //计算SM3哈希
// DigestRequest request = new DigestRequest();
// request.setAlgId(Gm0018AlgorithmIds.SM3);
// request.setData(payload.getBytes(StandardCharsets.UTF_8));
// BackupDataResult payloadHash = pcieCryptoService.digest(request);
//
//
// //todo 验签
// UserKeyVerifyRequest userKeyVerifyRequest = new UserKeyVerifyRequest();
// userKeyVerifyRequest.setKeyIndex(1);
// userKeyVerifyRequest.setData(payloadHash.getData());
// userKeyVerifyRequest.setSignature(decodeBlob(signValue, "signature is invalid"));
// try {
// pcieCryptoService.userKeyVerifyWithSm3(userKeyVerifyRequest);
// } catch (BizException ex) {
// throw ex;
// } catch (RuntimeException ex) {
// throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "signature verification failed");
// }
@ -202,8 +209,7 @@ public class OperationAuditService {
updateEntity.setAuditedBy(auditorUser);
updateEntity.setAuditedAt(LocalDateTime.now());
// 4. 执行更新
operationAuditLogRepository.update(updateEntity);
operationAuditLogRepository.updateByLogId(updateEntity);
log.info("日志复核完成, logId: {}, 审计人: {}, 结果: {}, {}", logId, auditorUser, req.getAuditResult(), req.getAuditComment());
}

View File

@ -3,7 +3,7 @@ package com.cisd.tms.modules.log.service;
import com.cisd.tms.integration.crypto.pcie.Gm0018AlgorithmIds;
import com.cisd.tms.integration.crypto.pcie.model.BackupDataResult;
import com.cisd.tms.integration.crypto.pcie.model.DigestRequest;
import com.cisd.tms.integration.crypto.pcie.model.EccInternalSignRequest;
import com.cisd.tms.integration.crypto.pcie.model.UserKeySignRequest;
import com.cisd.tms.integration.crypto.pcie.service.PcieCryptoService;
import com.cisd.tms.modules.log.entity.OperationAuditLogEntity;
import org.springframework.stereotype.Component;
@ -49,11 +49,11 @@ public class OperationAuditSigner {
entity.setPayloadHash(Base64.getEncoder().encodeToString(payloadHash.getData()));
// 3. 计算 SM2 签名值
EccInternalSignRequest signRequest = new EccInternalSignRequest();
signRequest.setData(payloadHash.getData());
signRequest.setKeyIndex(1);
BackupDataResult sign = pcieCryptoService.eccInternalSign(signRequest);
//计算 SM2 签名值
UserKeySignRequest userKeySignRequest = new UserKeySignRequest();
userKeySignRequest.setData(payloadHash.getData());
userKeySignRequest.setKeyIndex(1);
BackupDataResult sign = pcieCryptoService.userKeySignWithSm3(userKeySignRequest );
entity.setSignValue(Base64.getEncoder().encodeToString(sign.getData()));
}

View File

@ -46,7 +46,7 @@ class OperationAuditControllerTest {
void testReviewLog_Success_WithAuditAdminRole() throws Exception {
AuditLogReviewRequest req = new AuditLogReviewRequest();
req.setAuditResult(AuditResult.PASS);
req.setAuditSign("E1A2B3C4...");
// req.setAuditSign("E1A2B3C4...");
Mockito.doNothing().when(auditService).reviewLog(eq("log-789"), any(), eq("AUDIT_ADMIN"));