Merge remote-tracking branch 'origin/V1.00' into V1.00

This commit is contained in:
waner 2026-05-20 15:48:30 +08:00
commit 155252ef49
4 changed files with 98 additions and 45 deletions

View File

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

View File

@ -57,7 +57,7 @@ public class OperationAuditLogPageResponse {
// @Schema(description = "请求数据体 SM3 摘要", example = "1ab2c3d4e5f6...") // @Schema(description = "请求数据体 SM3 摘要", example = "1ab2c3d4e5f6...")
// private String payloadHash; // SM3摘要 // private String payloadHash; // SM3摘要
@Schema(description = "请求 SM2 签名值", example = "MEUC...") @Schema(description = "SM2 签名值", example = "MEUC...")
private String signValue; // SM2签名值 private String signValue; // SM2签名值
@Schema(description = "操作实际发生时间", type = "string", example = "2026-04-16 09:10:16") @Schema(description = "操作实际发生时间", type = "string", example = "2026-04-16 09:10:16")

View File

@ -54,8 +54,8 @@ public class OperationAuditLogResponse {
// @Schema(description = "请求数据体 SM3 摘要", example = "1ab2c3d4e5f6...") // @Schema(description = "请求数据体 SM3 摘要", example = "1ab2c3d4e5f6...")
// private String payloadHash; // SM3摘要 // private String payloadHash; // SM3摘要
@Schema(description = "请求 SM2 签名值", example = "MEUC...") @Schema(description = "SM2 签名值", example = "MEUC...")
private String signValue; // SM2签名值 private String signValue; // SM2签名值
@Schema(description = "操作实际发生时间", type = "string", format = "date-time", example = "2026-04-16 23:59:59") @Schema(description = "操作实际发生时间", type = "string", format = "date-time", example = "2026-04-16 23:59:59")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")

View File

@ -3,17 +3,16 @@ package com.cisd.tms.modules.log.service;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.cisd.tms.common.enums.ErrorCode; import com.cisd.tms.common.enums.ErrorCode;
import com.cisd.tms.common.exception.BizException; import com.cisd.tms.common.exception.BizException;
import com.cisd.tms.integration.crypto.pcie.service.PcieCryptoService; import com.cisd.tms.modules.auth.entity.RoleUkeyBindingEntity;
import com.cisd.tms.modules.auth.repository.RoleUkeyBindingRepository;
import com.cisd.tms.modules.log.annotation.AuditedOperation; import com.cisd.tms.modules.log.annotation.AuditedOperation;
import com.cisd.tms.modules.log.dto.*; import com.cisd.tms.modules.log.dto.*;
import com.cisd.tms.modules.log.entity.OperationAuditLogEntity; import com.cisd.tms.modules.log.entity.OperationAuditLogEntity;
import com.cisd.tms.modules.log.enums.AuditStatus; import com.cisd.tms.modules.log.enums.*;
import com.cisd.tms.modules.log.enums.AuthLevel;
import com.cisd.tms.modules.log.enums.OperationResult;
import com.cisd.tms.modules.log.enums.OperatorRoleCode;
import com.cisd.tms.modules.log.repository.OperationAuditLogRepository; import com.cisd.tms.modules.log.repository.OperationAuditLogRepository;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.encoders.Hex;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -22,25 +21,37 @@ import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.web.method.HandlerMethod; import org.springframework.web.method.HandlerMethod;
import java.nio.charset.StandardCharsets;
import java.security.PublicKey;
import java.security.Security;
import java.security.Signature;
import java.security.spec.X509EncodedKeySpec;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.Base64; import java.util.Base64;
import java.util.List;
import java.util.UUID; import java.util.UUID;
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
public class OperationAuditService { public class OperationAuditService {
public static final String ATTR_ROLE_CODE = "CURRENT_ROLE_CODE"; private static final String PROVIDER = "BC";
public static final String ATTR_AUTH_LEVEL = "CURRENT_AUTH_LEVEL"; private static final String SIGNATURE_ALGO = "SM3withSM2";
private static final String ATTR_ROLE_CODE = "CURRENT_ROLE_CODE";
private static final String ATTR_AUTH_LEVEL = "CURRENT_AUTH_LEVEL";
private static final Logger log = LoggerFactory.getLogger(OperationAuditService.class); private static final Logger log = LoggerFactory.getLogger(OperationAuditService.class);
private final OperationAuditLogRepository operationAuditLogRepository; private final OperationAuditLogRepository operationAuditLogRepository;
private final RoleUkeyBindingRepository roleUkeyBindingRepository;
private final OperationAuditSigner auditSigner; private final OperationAuditSigner auditSigner;
private final PcieCryptoService pcieCryptoService;
static {
if (Security.getProvider(PROVIDER) == null) {
Security.addProvider(new BouncyCastleProvider());
}
}
public void record(OperationAuditCommand command, boolean isSensitive) { public void record(OperationAuditCommand command, boolean isSensitive) {
try { try {
@ -183,45 +194,87 @@ public class OperationAuditService {
OperationAuditLogEntity existLog = operationAuditLogRepository.findByLogId(logId) OperationAuditLogEntity existLog = operationAuditLogRepository.findByLogId(logId)
.orElseThrow(() -> new BizException(ErrorCode.VALIDATE_FAILED.getCode(), "未找到该logId")); .orElseThrow(() -> new BizException(ErrorCode.VALIDATE_FAILED.getCode(), "未找到该logId"));
// String signValue = trim(req.getAuditSign());
// if (signValue.isEmpty()){
// throw new IllegalArgumentException("签名值不能为空");
// }
//
// String payload = auditSigner.buildAuditPayload(existLog);
//
// UserKeyVerifyRequest userKeyVerifyRequest = new UserKeyVerifyRequest();
// userKeyVerifyRequest.setKeyIndex(1);
// userKeyVerifyRequest.setData(payload.getBytes(StandardCharsets.UTF_8));
// 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 执行失败");
// }
if (!AuditStatus.PENDING.equals(existLog.getAuditStatus())) { if (!AuditStatus.PENDING.equals(existLog.getAuditStatus())) {
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "该日志已审计,禁止重复操作"); throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "该日志已审计,禁止重复操作");
} }
OperationAuditLogEntity updateEntity = new OperationAuditLogEntity(); String auditSign = trim(req.getAuditSign());
updateEntity.setLogId(existLog.getLogId()); if (auditSign.isEmpty()){
updateEntity.setAuditStatus(AuditStatus.REVIEWED); throw new IllegalArgumentException("签名值不能为空");
updateEntity.setAuditResult(req.getAuditResult()); }
updateEntity.setAuditComment(trim(req.getAuditComment()));
updateEntity.setAuditedBy(auditorUser); String signValueSource = buildSignValueSource(existLog, req.getAuditResult());
updateEntity.setAuditedAt(LocalDateTime.now());
List<RoleUkeyBindingEntity> list = roleUkeyBindingRepository.findActiveByRoleCode("AUDIT_ADMIN");
if (list.isEmpty()){
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "未找到审计管理员UKey");
}
RoleUkeyBindingEntity roleUkeyBindingEntity = list.get(0);
String publicKey = roleUkeyBindingEntity.getUkeyPubkey();
try{
verifySm2Signature(signValueSource, auditSign, publicKey);
} catch (Exception e){
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), e.getMessage());
}
try{
OperationAuditLogEntity updateEntity = new OperationAuditLogEntity();
updateEntity.setLogId(existLog.getLogId());
updateEntity.setAuditStatus(AuditStatus.REVIEWED);
updateEntity.setAuditResult(req.getAuditResult());
updateEntity.setAuditComment(trim(req.getAuditComment()));
updateEntity.setAuditedBy(auditorUser);
updateEntity.setAuditedAt(LocalDateTime.now());
operationAuditLogRepository.updateByLogId(updateEntity);
} catch (Exception e){
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "日志审计更新失败");
}
operationAuditLogRepository.updateByLogId(updateEntity);
log.info("日志复核完成, logId: {}, 审计人: {}, 结果: {}, {}", logId, auditorUser, req.getAuditResult(), req.getAuditComment()); log.info("日志复核完成, logId: {}, 审计人: {}, 结果: {}, {}", logId, auditorUser, req.getAuditResult(), req.getAuditComment());
} }
private static String buildSignValueSource(OperationAuditLogEntity existLog, AuditResult auditResult) {
String signValueSource = trim(existLog.getLogId()) +
existLog.getOperatorRoleCode() +
existLog.getOperatorAuthLevel() +
existLog.getModuleCode() +
existLog.getActionType() +
trim(existLog.getRemoteIp()) +
existLog.getOperationResult() +
trim(existLog.getSummary()) +
trim(existLog.getErrorMessage()) +
existLog.getAuditStatus() +
auditResult +
trim(existLog.getSignValue()) +
existLog.getOccurredAt();
return signValueSource;
}
public boolean verifySm2Signature(String data, String signatureBase64, String publicKeyBase64) {
try {
byte[] keyBytes = Base64.getDecoder().decode(publicKeyBase64);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
java.security.KeyFactory keyFactory = java.security.KeyFactory.getInstance("EC", PROVIDER);
PublicKey publicKey = keyFactory.generatePublic(keySpec);
Signature signature = Signature.getInstance(SIGNATURE_ALGO, PROVIDER);
signature.initVerify(publicKey);
signature.update(data.getBytes(StandardCharsets.UTF_8));
byte[] signatureBytes = Base64.getDecoder().decode(signatureBase64);
return signature.verify(signatureBytes);
} catch (Exception e) {
throw new RuntimeException("SM2验签失败", e);
}
}
private byte[] decodeBlob(String value, String message) { private byte[] decodeBlob(String value, String message) {
String normalized = value == null ? "" : value.trim(); String normalized = value == null ? "" : value.trim();
if (normalized.isEmpty()) { if (normalized.isEmpty()) {
@ -238,7 +291,7 @@ public class OperationAuditService {
} }
private String trim(String s){ private static String trim(String s){
return s == null ? "" : s.trim(); return s == null ? "" : s.trim();
} }
} }