33 changed files with 438 additions and 827 deletions
@ -1,78 +0,0 @@ |
|||||
package com.zdxt.enforcementcode.domain.bo; |
|
||||
|
|
||||
import com.zdxt.enforcementcode.domain.bean.CryptoIntegrityData; |
|
||||
import org.dromara.common.mybatis.core.domain.BaseEntity; |
|
||||
import org.dromara.common.core.validate.AddGroup; |
|
||||
import org.dromara.common.core.validate.EditGroup; |
|
||||
import io.github.linpeilie.annotations.AutoMapper; |
|
||||
import lombok.Data; |
|
||||
import lombok.EqualsAndHashCode; |
|
||||
import jakarta.validation.constraints.*; |
|
||||
import java.util.Date; |
|
||||
|
|
||||
/** |
|
||||
* 数据完整性校验业务对象 crypto_integrity_data |
|
||||
* |
|
||||
* @author vincent |
|
||||
* @date 2026-09-28 |
|
||||
*/ |
|
||||
@Data |
|
||||
@EqualsAndHashCode(callSuper = true) |
|
||||
@AutoMapper(target = CryptoIntegrityData.class, reverseConvertGenerate = false) |
|
||||
public class CryptoIntegrityDataBo extends BaseEntity { |
|
||||
|
|
||||
/** |
|
||||
* 主键ID |
|
||||
*/ |
|
||||
@NotNull(message = "主键ID不能为空", groups = { EditGroup.class }) |
|
||||
private Long id; |
|
||||
|
|
||||
/** |
|
||||
* 业务表名,如 sys_user |
|
||||
*/ |
|
||||
@NotBlank(message = "业务表名不能为空", groups = { AddGroup.class, EditGroup.class }) |
|
||||
private String tableName; |
|
||||
|
|
||||
/** |
|
||||
* 业务记录主键,字符串兼容数字/UUID |
|
||||
*/ |
|
||||
@NotBlank(message = "业务记录主键不能为空", groups = { AddGroup.class, EditGroup.class }) |
|
||||
private String bizId; |
|
||||
|
|
||||
/** |
|
||||
* 参与完整性摘要的字段列表,逗号分隔:username,id_card,phone |
|
||||
*/ |
|
||||
@NotBlank(message = "参与完整性摘要的字段列表不能为空", groups = { AddGroup.class, EditGroup.class }) |
|
||||
private String fieldList; |
|
||||
|
|
||||
/** |
|
||||
* 业务字段拼接明文的SM3摘要值,用于完整性校验 |
|
||||
*/ |
|
||||
@NotBlank(message = "SM3摘要值不能为空", groups = { AddGroup.class, EditGroup.class }) |
|
||||
private String sm3Hash; |
|
||||
|
|
||||
/** |
|
||||
* 完整性哈希算法:SM3 |
|
||||
*/ |
|
||||
private String hashAlg; |
|
||||
|
|
||||
/** |
|
||||
* 摘要生成时间 |
|
||||
*/ |
|
||||
private Date generateTime; |
|
||||
|
|
||||
/** |
|
||||
* 最近一次完整性校验时间 |
|
||||
*/ |
|
||||
private Date lastVerifyTime; |
|
||||
|
|
||||
/** |
|
||||
* 校验结果:0未校验 1校验通过 2校验失败(篡改) |
|
||||
*/ |
|
||||
private Integer verifyResult; |
|
||||
|
|
||||
/** |
|
||||
* 逻辑删除 |
|
||||
*/ |
|
||||
private Integer isDeleted; |
|
||||
} |
|
||||
@ -1,83 +0,0 @@ |
|||||
package com.zdxt.enforcementcode.domain.bo; |
|
||||
|
|
||||
import com.zdxt.enforcementcode.domain.bean.CryptoOperationAudit; |
|
||||
import org.dromara.common.mybatis.core.domain.BaseEntity; |
|
||||
import org.dromara.common.core.validate.AddGroup; |
|
||||
import org.dromara.common.core.validate.EditGroup; |
|
||||
import io.github.linpeilie.annotations.AutoMapper; |
|
||||
import lombok.Data; |
|
||||
import lombok.EqualsAndHashCode; |
|
||||
import jakarta.validation.constraints.*; |
|
||||
import java.util.Date; |
|
||||
|
|
||||
/** |
|
||||
* 密评敏感字段操作审计业务对象 crypto_operation_audit |
|
||||
* |
|
||||
* @author vincent |
|
||||
* @date 2026-09-28 |
|
||||
*/ |
|
||||
@Data |
|
||||
@EqualsAndHashCode(callSuper = true) |
|
||||
@AutoMapper(target = CryptoOperationAudit.class, reverseConvertGenerate = false) |
|
||||
public class CryptoOperationAuditBo extends BaseEntity { |
|
||||
|
|
||||
/** |
|
||||
* 主键ID |
|
||||
*/ |
|
||||
@NotNull(message = "主键ID不能为空", groups = { EditGroup.class }) |
|
||||
private Long id; |
|
||||
|
|
||||
/** |
|
||||
* 关联 crypto_sensitive_data 主键ID |
|
||||
*/ |
|
||||
private Long sensitiveDataId; |
|
||||
|
|
||||
/** |
|
||||
* 业务表名 |
|
||||
*/ |
|
||||
@NotBlank(message = "业务表名不能为空", groups = { AddGroup.class, EditGroup.class }) |
|
||||
private String tableName; |
|
||||
|
|
||||
/** |
|
||||
* 业务记录主键 |
|
||||
*/ |
|
||||
@NotBlank(message = "业务记录主键不能为空", groups = { AddGroup.class, EditGroup.class }) |
|
||||
private String bizId; |
|
||||
|
|
||||
/** |
|
||||
* 敏感字段编码 |
|
||||
*/ |
|
||||
@NotBlank(message = "敏感字段编码不能为空", groups = { AddGroup.class, EditGroup.class }) |
|
||||
private String fieldCode; |
|
||||
|
|
||||
/** |
|
||||
* 操作类型:1新增密文 2解密查看 3修改密文 4密钥轮换 |
|
||||
*/ |
|
||||
@NotNull(message = "操作类型不能为空", groups = { AddGroup.class, EditGroup.class }) |
|
||||
private Integer operateType; |
|
||||
|
|
||||
/** |
|
||||
* 操作人ID(系统用户ID) |
|
||||
*/ |
|
||||
private Long operatorId; |
|
||||
|
|
||||
/** |
|
||||
* 操作客户端IP地址 |
|
||||
*/ |
|
||||
private String operatorIp; |
|
||||
|
|
||||
/** |
|
||||
* 操作发生时间 |
|
||||
*/ |
|
||||
private Date operateTime; |
|
||||
|
|
||||
/** |
|
||||
* 操作备注信息 |
|
||||
*/ |
|
||||
private String remark; |
|
||||
|
|
||||
/** |
|
||||
* 逻辑删除 |
|
||||
*/ |
|
||||
private Integer isDeleted; |
|
||||
} |
|
||||
@ -1,92 +0,0 @@ |
|||||
package com.zdxt.enforcementcode.domain.bo; |
|
||||
|
|
||||
import com.zdxt.enforcementcode.domain.bean.CryptoSensitiveData; |
|
||||
import org.dromara.common.mybatis.core.domain.BaseEntity; |
|
||||
import org.dromara.common.core.validate.AddGroup; |
|
||||
import org.dromara.common.core.validate.EditGroup; |
|
||||
import io.github.linpeilie.annotations.AutoMapper; |
|
||||
import lombok.Data; |
|
||||
import lombok.EqualsAndHashCode; |
|
||||
import jakarta.validation.constraints.*; |
|
||||
|
|
||||
/** |
|
||||
* 密评敏感字段密文存储业务对象 crypto_sensitive_data |
|
||||
* |
|
||||
* @author vincent |
|
||||
* @date 2026-09-28 |
|
||||
*/ |
|
||||
@Data |
|
||||
@EqualsAndHashCode(callSuper = true) |
|
||||
@AutoMapper(target = CryptoSensitiveData.class, reverseConvertGenerate = false) |
|
||||
public class CryptoSensitiveDataBo extends BaseEntity { |
|
||||
|
|
||||
/** |
|
||||
* 主键ID |
|
||||
*/ |
|
||||
@NotNull(message = "主键ID不能为空", groups = { EditGroup.class }) |
|
||||
private Long id; |
|
||||
|
|
||||
/** |
|
||||
* 业务表名,如 sys_user |
|
||||
*/ |
|
||||
@NotBlank(message = "业务表名不能为空", groups = { AddGroup.class, EditGroup.class }) |
|
||||
private String tableName; |
|
||||
|
|
||||
/** |
|
||||
* 业务表主键ID,字符串兼容数字/UUID主键 |
|
||||
*/ |
|
||||
@NotBlank(message = "业务表主键ID不能为空", groups = { AddGroup.class, EditGroup.class }) |
|
||||
private String bizId; |
|
||||
|
|
||||
/** |
|
||||
* 业务字段编码,username/id_card/phone/password |
|
||||
*/ |
|
||||
@NotBlank(message = "业务字段编码不能为空", groups = { AddGroup.class, EditGroup.class }) |
|
||||
private String fieldCode; |
|
||||
|
|
||||
/** |
|
||||
* 字段原始值 |
|
||||
*/ |
|
||||
private String fieldValue; |
|
||||
|
|
||||
/** |
|
||||
* SM4-CBC密文Base64;password单向哈希场景此字段为空 |
|
||||
*/ |
|
||||
private String cipherText; |
|
||||
|
|
||||
/** |
|
||||
* SM4-CBC初始化向量,16字节随机值Base64;password此字段为空 |
|
||||
*/ |
|
||||
private String fieldIv; |
|
||||
|
|
||||
/** |
|
||||
* 明文SM3哈希,仅用于等值查询,不可逆 |
|
||||
*/ |
|
||||
private String plainHash; |
|
||||
|
|
||||
/** |
|
||||
* 哈希盐值,仅password单向哈希字段使用 |
|
||||
*/ |
|
||||
private String fieldSalt; |
|
||||
|
|
||||
/** |
|
||||
* 加密算法:SM4-CBC / SM3 |
|
||||
*/ |
|
||||
private String encryptAlg; |
|
||||
|
|
||||
/** |
|
||||
* 密钥版本号,用于密钥轮换,密评必查 |
|
||||
*/ |
|
||||
@NotBlank(message = "密钥版本号不能为空", groups = { AddGroup.class, EditGroup.class }) |
|
||||
private String keyVersion; |
|
||||
|
|
||||
/** |
|
||||
* 是否为当前有效密文:1有效,0历史旧密文(密钥轮换保留) |
|
||||
*/ |
|
||||
private Integer isActive; |
|
||||
|
|
||||
/** |
|
||||
* 逻辑删除:0未删除,1已删除 |
|
||||
*/ |
|
||||
private Integer isDeleted; |
|
||||
} |
|
||||
@ -1,24 +0,0 @@ |
|||||
package com.zdxt.enforcementcode.service; |
|
||||
|
|
||||
import com.zdxt.enforcementcode.domain.vo.CryptoIntegrityDataVo; |
|
||||
import com.zdxt.enforcementcode.domain.bo.CryptoIntegrityDataBo; |
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo; |
|
||||
import org.dromara.common.mybatis.core.page.PageQuery; |
|
||||
|
|
||||
import java.util.Collection; |
|
||||
import java.util.List; |
|
||||
|
|
||||
public interface ICryptoIntegrityDataService { |
|
||||
|
|
||||
CryptoIntegrityDataVo queryById(Long id); |
|
||||
|
|
||||
TableDataInfo<CryptoIntegrityDataVo> queryPageList(CryptoIntegrityDataBo bo, PageQuery pageQuery); |
|
||||
|
|
||||
List<CryptoIntegrityDataVo> queryList(CryptoIntegrityDataBo bo); |
|
||||
|
|
||||
Boolean insertByBo(CryptoIntegrityDataBo bo); |
|
||||
|
|
||||
Boolean updateByBo(CryptoIntegrityDataBo bo); |
|
||||
|
|
||||
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid); |
|
||||
} |
|
||||
@ -1,24 +0,0 @@ |
|||||
package com.zdxt.enforcementcode.service; |
|
||||
|
|
||||
import com.zdxt.enforcementcode.domain.vo.CryptoOperationAuditVo; |
|
||||
import com.zdxt.enforcementcode.domain.bo.CryptoOperationAuditBo; |
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo; |
|
||||
import org.dromara.common.mybatis.core.page.PageQuery; |
|
||||
|
|
||||
import java.util.Collection; |
|
||||
import java.util.List; |
|
||||
|
|
||||
public interface ICryptoOperationAuditService { |
|
||||
|
|
||||
CryptoOperationAuditVo queryById(Long id); |
|
||||
|
|
||||
TableDataInfo<CryptoOperationAuditVo> queryPageList(CryptoOperationAuditBo bo, PageQuery pageQuery); |
|
||||
|
|
||||
List<CryptoOperationAuditVo> queryList(CryptoOperationAuditBo bo); |
|
||||
|
|
||||
Boolean insertByBo(CryptoOperationAuditBo bo); |
|
||||
|
|
||||
Boolean updateByBo(CryptoOperationAuditBo bo); |
|
||||
|
|
||||
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid); |
|
||||
} |
|
||||
@ -1,24 +0,0 @@ |
|||||
package com.zdxt.enforcementcode.service; |
|
||||
|
|
||||
import com.zdxt.enforcementcode.domain.vo.CryptoSensitiveDataVo; |
|
||||
import com.zdxt.enforcementcode.domain.bo.CryptoSensitiveDataBo; |
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo; |
|
||||
import org.dromara.common.mybatis.core.page.PageQuery; |
|
||||
|
|
||||
import java.util.Collection; |
|
||||
import java.util.List; |
|
||||
|
|
||||
public interface ICryptoSensitiveDataService { |
|
||||
|
|
||||
CryptoSensitiveDataVo queryById(Long id); |
|
||||
|
|
||||
TableDataInfo<CryptoSensitiveDataVo> queryPageList(CryptoSensitiveDataBo bo, PageQuery pageQuery); |
|
||||
|
|
||||
List<CryptoSensitiveDataVo> queryList(CryptoSensitiveDataBo bo); |
|
||||
|
|
||||
Boolean insertByBo(CryptoSensitiveDataBo bo); |
|
||||
|
|
||||
Boolean updateByBo(CryptoSensitiveDataBo bo); |
|
||||
|
|
||||
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid); |
|
||||
} |
|
||||
@ -1,89 +0,0 @@ |
|||||
package com.zdxt.enforcementcode.service.impl; |
|
||||
|
|
||||
import com.zdxt.enforcementcode.domain.bean.CryptoIntegrityData; |
|
||||
import org.dromara.common.core.utils.MapstructUtils; |
|
||||
import org.dromara.common.core.utils.StringUtils; |
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo; |
|
||||
import org.dromara.common.mybatis.core.page.PageQuery; |
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
|
||||
import lombok.RequiredArgsConstructor; |
|
||||
import lombok.extern.slf4j.Slf4j; |
|
||||
import org.springframework.stereotype.Service; |
|
||||
import com.zdxt.enforcementcode.domain.bo.CryptoIntegrityDataBo; |
|
||||
import com.zdxt.enforcementcode.domain.vo.CryptoIntegrityDataVo; |
|
||||
import com.zdxt.enforcementcode.mapper.CryptoIntegrityDataMapper; |
|
||||
import com.zdxt.enforcementcode.service.ICryptoIntegrityDataService; |
|
||||
|
|
||||
import java.util.List; |
|
||||
import java.util.Map; |
|
||||
import java.util.Collection; |
|
||||
|
|
||||
@Slf4j |
|
||||
@RequiredArgsConstructor |
|
||||
@Service |
|
||||
public class CryptoIntegrityDataServiceImpl implements ICryptoIntegrityDataService { |
|
||||
|
|
||||
private final CryptoIntegrityDataMapper baseMapper; |
|
||||
|
|
||||
@Override |
|
||||
public CryptoIntegrityDataVo queryById(Long id) { |
|
||||
return baseMapper.selectVoById(id); |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public TableDataInfo<CryptoIntegrityDataVo> queryPageList(CryptoIntegrityDataBo bo, PageQuery pageQuery) { |
|
||||
LambdaQueryWrapper<CryptoIntegrityData> lqw = buildQueryWrapper(bo); |
|
||||
Page<CryptoIntegrityDataVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw); |
|
||||
return TableDataInfo.build(result); |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public List<CryptoIntegrityDataVo> queryList(CryptoIntegrityDataBo bo) { |
|
||||
LambdaQueryWrapper<CryptoIntegrityData> lqw = buildQueryWrapper(bo); |
|
||||
return baseMapper.selectVoList(lqw); |
|
||||
} |
|
||||
|
|
||||
private LambdaQueryWrapper<CryptoIntegrityData> buildQueryWrapper(CryptoIntegrityDataBo bo) { |
|
||||
Map<String, Object> params = bo.getParams(); |
|
||||
LambdaQueryWrapper<CryptoIntegrityData> lqw = Wrappers.lambdaQuery(); |
|
||||
lqw.orderByAsc(CryptoIntegrityData::getId); |
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getTableName()), CryptoIntegrityData::getTableName, bo.getTableName()); |
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getBizId()), CryptoIntegrityData::getBizId, bo.getBizId()); |
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getHashAlg()), CryptoIntegrityData::getHashAlg, bo.getHashAlg()); |
|
||||
lqw.eq(bo.getVerifyResult() != null, CryptoIntegrityData::getVerifyResult, bo.getVerifyResult()); |
|
||||
lqw.eq(bo.getIsDeleted() != null, CryptoIntegrityData::getIsDeleted, bo.getIsDeleted()); |
|
||||
return lqw; |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public Boolean insertByBo(CryptoIntegrityDataBo bo) { |
|
||||
CryptoIntegrityData add = MapstructUtils.convert(bo, CryptoIntegrityData.class); |
|
||||
validEntityBeforeSave(add); |
|
||||
boolean flag = baseMapper.insert(add) > 0; |
|
||||
if (flag) { |
|
||||
bo.setId(add.getId()); |
|
||||
} |
|
||||
return flag; |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public Boolean updateByBo(CryptoIntegrityDataBo bo) { |
|
||||
CryptoIntegrityData update = MapstructUtils.convert(bo, CryptoIntegrityData.class); |
|
||||
validEntityBeforeSave(update); |
|
||||
return baseMapper.updateById(update) > 0; |
|
||||
} |
|
||||
|
|
||||
private void validEntityBeforeSave(CryptoIntegrityData entity) { |
|
||||
//TODO 做一些数据校验,如唯一约束
|
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) { |
|
||||
if (isValid) { |
|
||||
//TODO 做一些业务上的校验,判断是否需要校验
|
|
||||
} |
|
||||
return baseMapper.deleteByIds(ids) > 0; |
|
||||
} |
|
||||
} |
|
||||
@ -1,91 +0,0 @@ |
|||||
package com.zdxt.enforcementcode.service.impl; |
|
||||
|
|
||||
import com.zdxt.enforcementcode.domain.bean.CryptoOperationAudit; |
|
||||
import org.dromara.common.core.utils.MapstructUtils; |
|
||||
import org.dromara.common.core.utils.StringUtils; |
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo; |
|
||||
import org.dromara.common.mybatis.core.page.PageQuery; |
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
|
||||
import lombok.RequiredArgsConstructor; |
|
||||
import lombok.extern.slf4j.Slf4j; |
|
||||
import org.springframework.stereotype.Service; |
|
||||
import com.zdxt.enforcementcode.domain.bo.CryptoOperationAuditBo; |
|
||||
import com.zdxt.enforcementcode.domain.vo.CryptoOperationAuditVo; |
|
||||
import com.zdxt.enforcementcode.mapper.CryptoOperationAuditMapper; |
|
||||
import com.zdxt.enforcementcode.service.ICryptoOperationAuditService; |
|
||||
|
|
||||
import java.util.List; |
|
||||
import java.util.Map; |
|
||||
import java.util.Collection; |
|
||||
|
|
||||
@Slf4j |
|
||||
@RequiredArgsConstructor |
|
||||
@Service |
|
||||
public class CryptoOperationAuditServiceImpl implements ICryptoOperationAuditService { |
|
||||
|
|
||||
private final CryptoOperationAuditMapper baseMapper; |
|
||||
|
|
||||
@Override |
|
||||
public CryptoOperationAuditVo queryById(Long id) { |
|
||||
return baseMapper.selectVoById(id); |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public TableDataInfo<CryptoOperationAuditVo> queryPageList(CryptoOperationAuditBo bo, PageQuery pageQuery) { |
|
||||
LambdaQueryWrapper<CryptoOperationAudit> lqw = buildQueryWrapper(bo); |
|
||||
Page<CryptoOperationAuditVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw); |
|
||||
return TableDataInfo.build(result); |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public List<CryptoOperationAuditVo> queryList(CryptoOperationAuditBo bo) { |
|
||||
LambdaQueryWrapper<CryptoOperationAudit> lqw = buildQueryWrapper(bo); |
|
||||
return baseMapper.selectVoList(lqw); |
|
||||
} |
|
||||
|
|
||||
private LambdaQueryWrapper<CryptoOperationAudit> buildQueryWrapper(CryptoOperationAuditBo bo) { |
|
||||
Map<String, Object> params = bo.getParams(); |
|
||||
LambdaQueryWrapper<CryptoOperationAudit> lqw = Wrappers.lambdaQuery(); |
|
||||
lqw.orderByAsc(CryptoOperationAudit::getId); |
|
||||
lqw.eq(bo.getSensitiveDataId() != null, CryptoOperationAudit::getSensitiveDataId, bo.getSensitiveDataId()); |
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getTableName()), CryptoOperationAudit::getTableName, bo.getTableName()); |
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getBizId()), CryptoOperationAudit::getBizId, bo.getBizId()); |
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getFieldCode()), CryptoOperationAudit::getFieldCode, bo.getFieldCode()); |
|
||||
lqw.eq(bo.getOperateType() != null, CryptoOperationAudit::getOperateType, bo.getOperateType()); |
|
||||
lqw.eq(bo.getOperatorId() != null, CryptoOperationAudit::getOperatorId, bo.getOperatorId()); |
|
||||
lqw.eq(bo.getIsDeleted() != null, CryptoOperationAudit::getIsDeleted, bo.getIsDeleted()); |
|
||||
return lqw; |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public Boolean insertByBo(CryptoOperationAuditBo bo) { |
|
||||
CryptoOperationAudit add = MapstructUtils.convert(bo, CryptoOperationAudit.class); |
|
||||
validEntityBeforeSave(add); |
|
||||
boolean flag = baseMapper.insert(add) > 0; |
|
||||
if (flag) { |
|
||||
bo.setId(add.getId()); |
|
||||
} |
|
||||
return flag; |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public Boolean updateByBo(CryptoOperationAuditBo bo) { |
|
||||
CryptoOperationAudit update = MapstructUtils.convert(bo, CryptoOperationAudit.class); |
|
||||
validEntityBeforeSave(update); |
|
||||
return baseMapper.updateById(update) > 0; |
|
||||
} |
|
||||
|
|
||||
private void validEntityBeforeSave(CryptoOperationAudit entity) { |
|
||||
//TODO 做一些数据校验,如唯一约束
|
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) { |
|
||||
if (isValid) { |
|
||||
//TODO 做一些业务上的校验,判断是否需要校验
|
|
||||
} |
|
||||
return baseMapper.deleteByIds(ids) > 0; |
|
||||
} |
|
||||
} |
|
||||
@ -1,91 +0,0 @@ |
|||||
package com.zdxt.enforcementcode.service.impl; |
|
||||
|
|
||||
import com.zdxt.enforcementcode.domain.bean.CryptoSensitiveData; |
|
||||
import org.dromara.common.core.utils.MapstructUtils; |
|
||||
import org.dromara.common.core.utils.StringUtils; |
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo; |
|
||||
import org.dromara.common.mybatis.core.page.PageQuery; |
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
|
||||
import lombok.RequiredArgsConstructor; |
|
||||
import lombok.extern.slf4j.Slf4j; |
|
||||
import org.springframework.stereotype.Service; |
|
||||
import com.zdxt.enforcementcode.domain.bo.CryptoSensitiveDataBo; |
|
||||
import com.zdxt.enforcementcode.domain.vo.CryptoSensitiveDataVo; |
|
||||
import com.zdxt.enforcementcode.mapper.CryptoSensitiveDataMapper; |
|
||||
import com.zdxt.enforcementcode.service.ICryptoSensitiveDataService; |
|
||||
|
|
||||
import java.util.List; |
|
||||
import java.util.Map; |
|
||||
import java.util.Collection; |
|
||||
|
|
||||
@Slf4j |
|
||||
@RequiredArgsConstructor |
|
||||
@Service |
|
||||
public class CryptoSensitiveDataServiceImpl implements ICryptoSensitiveDataService { |
|
||||
|
|
||||
private final CryptoSensitiveDataMapper baseMapper; |
|
||||
|
|
||||
@Override |
|
||||
public CryptoSensitiveDataVo queryById(Long id) { |
|
||||
return baseMapper.selectVoById(id); |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public TableDataInfo<CryptoSensitiveDataVo> queryPageList(CryptoSensitiveDataBo bo, PageQuery pageQuery) { |
|
||||
LambdaQueryWrapper<CryptoSensitiveData> lqw = buildQueryWrapper(bo); |
|
||||
Page<CryptoSensitiveDataVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw); |
|
||||
return TableDataInfo.build(result); |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public List<CryptoSensitiveDataVo> queryList(CryptoSensitiveDataBo bo) { |
|
||||
LambdaQueryWrapper<CryptoSensitiveData> lqw = buildQueryWrapper(bo); |
|
||||
return baseMapper.selectVoList(lqw); |
|
||||
} |
|
||||
|
|
||||
private LambdaQueryWrapper<CryptoSensitiveData> buildQueryWrapper(CryptoSensitiveDataBo bo) { |
|
||||
Map<String, Object> params = bo.getParams(); |
|
||||
LambdaQueryWrapper<CryptoSensitiveData> lqw = Wrappers.lambdaQuery(); |
|
||||
lqw.orderByAsc(CryptoSensitiveData::getId); |
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getTableName()), CryptoSensitiveData::getTableName, bo.getTableName()); |
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getBizId()), CryptoSensitiveData::getBizId, bo.getBizId()); |
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getFieldCode()), CryptoSensitiveData::getFieldCode, bo.getFieldCode()); |
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getEncryptAlg()), CryptoSensitiveData::getEncryptAlg, bo.getEncryptAlg()); |
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getKeyVersion()), CryptoSensitiveData::getKeyVersion, bo.getKeyVersion()); |
|
||||
lqw.eq(bo.getIsActive() != null, CryptoSensitiveData::getIsActive, bo.getIsActive()); |
|
||||
lqw.eq(bo.getIsDeleted() != null, CryptoSensitiveData::getIsDeleted, bo.getIsDeleted()); |
|
||||
return lqw; |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public Boolean insertByBo(CryptoSensitiveDataBo bo) { |
|
||||
CryptoSensitiveData add = MapstructUtils.convert(bo, CryptoSensitiveData.class); |
|
||||
validEntityBeforeSave(add); |
|
||||
boolean flag = baseMapper.insert(add) > 0; |
|
||||
if (flag) { |
|
||||
bo.setId(add.getId()); |
|
||||
} |
|
||||
return flag; |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public Boolean updateByBo(CryptoSensitiveDataBo bo) { |
|
||||
CryptoSensitiveData update = MapstructUtils.convert(bo, CryptoSensitiveData.class); |
|
||||
validEntityBeforeSave(update); |
|
||||
return baseMapper.updateById(update) > 0; |
|
||||
} |
|
||||
|
|
||||
private void validEntityBeforeSave(CryptoSensitiveData entity) { |
|
||||
//TODO 做一些数据校验,如唯一约束
|
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) { |
|
||||
if (isValid) { |
|
||||
//TODO 做一些业务上的校验,判断是否需要校验
|
|
||||
} |
|
||||
return baseMapper.deleteByIds(ids) > 0; |
|
||||
} |
|
||||
} |
|
||||
@ -1,166 +0,0 @@ |
|||||
package com.zdxt.enforcementcode.service.thirdparty; |
|
||||
|
|
||||
import com.zdxt.enforcementcode.domain.vo.passwordparty.SymmetryEncryptVo; |
|
||||
import com.zdxt.enforcementcode.service.thirdparty.impl.PasswordPartyServiceImpl; |
|
||||
|
|
||||
/** |
|
||||
* IPasswordPartyService 接口方法正向流程演示 |
|
||||
* <p> |
|
||||
* 运行前请确保: |
|
||||
* 1. HSM 密码服务平台已启动且网络可达 |
|
||||
* 2. 下方 BASE_URL 配置为正确的 HSM 服务地址 |
|
||||
* 3. HSM 中已配置好所需的密钥(密钥索引、算法类型等) |
|
||||
* </p> |
|
||||
* |
|
||||
* @author vincent |
|
||||
* @date 2026-09-22 |
|
||||
*/ |
|
||||
public class PasswordPartyDemo { |
|
||||
|
|
||||
/** HSM 密码服务平台地址,按实际环境修改 */ |
|
||||
private static final String BASE_URL = "http://localhost:8080"; |
|
||||
|
|
||||
private static IPasswordPartyService service; |
|
||||
|
|
||||
/** |
|
||||
* 主入口:手动构造 PasswordPartyServiceImpl 实例,依次调用各接口方法验证正向流程 |
|
||||
* <p> |
|
||||
* 执行顺序: |
|
||||
* 1. 生成随机数 |
|
||||
* 2. 对称加密 → 对称解密(验证互为逆操作) |
|
||||
* 3. SM2签名 |
|
||||
* 4. 非对称加密 → 非对称解密(验证互为逆操作) |
|
||||
* 5. HMAC摘要 |
|
||||
* </p> |
|
||||
*/ |
|
||||
public static void main(String[] args) { |
|
||||
// 构造配置并初始化 Service 实例(不依赖 Spring 容器)
|
|
||||
PasswordPartyConfig config = new PasswordPartyConfig(); |
|
||||
config.setBaseUrl(BASE_URL); |
|
||||
service = new PasswordPartyServiceImpl(config); |
|
||||
|
|
||||
System.out.println("========== IPasswordPartyService 正向流程演示 ==========\n"); |
|
||||
|
|
||||
// 1. 生成随机数:验证 HSM 基础连通性
|
|
||||
testGenerateRandom(); |
|
||||
// 2. 对称加密 + 对称解密:使用 SM4 算法,验证加解密互为逆操作
|
|
||||
testSymmetryEncryptAndDecrypt(); |
|
||||
// 3. SM2签名:对摘要数据进行签名,验证签名值非空
|
|
||||
testSM2SignAndVerify(); |
|
||||
// 4. 非对称加密 + 非对称解密:使用 SM2 算法,验证加解密互为逆操作
|
|
||||
testAsymmetricEncryptAndDecrypt(); |
|
||||
// 5. HMAC摘要:使用 SM3 算法计算消息认证码
|
|
||||
testHmacWithIndex(); |
|
||||
|
|
||||
System.out.println("\n========== 全部演示完成 =========="); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 1. 生成随机数 |
|
||||
*/ |
|
||||
private static void testGenerateRandom() { |
|
||||
System.out.println("----- 1. 生成随机数 -----"); |
|
||||
String random = service.generateRandom(10); |
|
||||
System.out.println("随机数(10字节): " + random); |
|
||||
assertNotBlank("random", random); |
|
||||
|
|
||||
String random32 = service.generateRandom(32); |
|
||||
System.out.println("随机数(32字节): " + random32); |
|
||||
assertNotBlank("random32", random32); |
|
||||
System.out.println(); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 2. 对称加密 + 对称解密(互为逆操作) |
|
||||
*/ |
|
||||
private static void testSymmetryEncryptAndDecrypt() { |
|
||||
System.out.println("----- 2. 对称加密 + 对称解密 -----"); |
|
||||
String plainHex = "31323334353637383132333435363738"; |
|
||||
|
|
||||
SymmetryEncryptVo encryptResult = service.symmetryEncrypt(plainHex); |
|
||||
System.out.println("对称加密原文: " + plainHex); |
|
||||
System.out.println("对称加密密文: " + encryptResult.getOutData()); |
|
||||
assertNotBlank("encryptOutData", encryptResult.getOutData()); |
|
||||
|
|
||||
SymmetryEncryptVo decryptResult = service.symmetryDecrypt(null, encryptResult.getOutData()); |
|
||||
System.out.println("对称解密结果: " + decryptResult.getOutData()); |
|
||||
assertNotBlank("decryptOutData", decryptResult.getOutData()); |
|
||||
|
|
||||
if (plainHex.equalsIgnoreCase(decryptResult.getOutData())) { |
|
||||
System.out.println("[PASS] 对称加密/解密互为逆操作,解密结果与原文一致"); |
|
||||
} else { |
|
||||
System.out.println("[WARN] 解密结果与原文不一致,请检查密钥配置"); |
|
||||
} |
|
||||
System.out.println(); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 3. SM2签名 + SM2验签 |
|
||||
*/ |
|
||||
private static void testSM2SignAndVerify() { |
|
||||
System.out.println("----- 3. SM2签名 + SM2验签 -----"); |
|
||||
String plainDataHex = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"; |
|
||||
|
|
||||
String signValue = service.sm2SignData(plainDataHex); |
|
||||
System.out.println("SM2签名原文(摘要): " + plainDataHex); |
|
||||
System.out.println("SM2签名值: " + signValue); |
|
||||
assertNotBlank("signData", signValue); |
|
||||
|
|
||||
System.out.println("[PASS] SM2签名成功,签名值非空"); |
|
||||
System.out.println(); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 4. 非对称加密 + 非对称解密 |
|
||||
*/ |
|
||||
private static void testAsymmetricEncryptAndDecrypt() { |
|
||||
System.out.println("----- 4. 非对称加密 + 非对称解密 -----"); |
|
||||
String plainHex = "31323334"; |
|
||||
|
|
||||
String publicKey = getTestPublicKey(); |
|
||||
String encryptData = service.externalEncrypt(publicKey, plainHex); |
|
||||
System.out.println("非对称加密原文: " + plainHex); |
|
||||
System.out.println("非对称加密密文: " + encryptData); |
|
||||
assertNotBlank("encryptData", encryptData); |
|
||||
|
|
||||
String decrypted = service.internalDecrypt(encryptData); |
|
||||
System.out.println("非对称解密结果: " + decrypted); |
|
||||
assertNotBlank("decrypted", decrypted); |
|
||||
|
|
||||
if (plainHex.equalsIgnoreCase(decrypted)) { |
|
||||
System.out.println("[PASS] 非对称加密/解密互为逆操作,解密结果与原文一致"); |
|
||||
} else { |
|
||||
System.out.println("[WARN] 解密结果与原文不一致,请检查密钥配置"); |
|
||||
} |
|
||||
System.out.println(); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 5. HMAC摘要 |
|
||||
*/ |
|
||||
private static void testHmacWithIndex() { |
|
||||
System.out.println("----- 5. HMAC摘要 -----"); |
|
||||
String inputDataHex = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"; |
|
||||
|
|
||||
String hmac = service.hmacWithIndex(inputDataHex); |
|
||||
System.out.println("HMAC输入摘要: " + inputDataHex); |
|
||||
System.out.println("HMAC输出值: " + hmac); |
|
||||
assertNotBlank("hmac", hmac); |
|
||||
System.out.println("[PASS] HMAC摘要成功,结果非空"); |
|
||||
System.out.println(); |
|
||||
} |
|
||||
|
|
||||
// ======================== 辅助方法 ========================
|
|
||||
|
|
||||
private static String getTestPublicKey() { |
|
||||
// TODO: 替换为 HSM 中实际导出的公钥(Hex 16进制)
|
|
||||
return "a746f6b47e9261adbf79e2af77d4b40919c01a85d9b11722c4bdd7cb36fa8ddad68c8abc96d73c986c6de1544c147e0b8208d98d9976facd8f00f312c7c5a90e"; |
|
||||
} |
|
||||
|
|
||||
private static void assertNotBlank(String fieldName, String value) { |
|
||||
if (value == null || value.isBlank()) { |
|
||||
throw new AssertionError("[FAIL] " + fieldName + " 返回值为空"); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
} |
|
||||
@ -0,0 +1,19 @@ |
|||||
|
package org.dromara.system.config; |
||||
|
|
||||
|
import org.springframework.boot.context.properties.ConfigurationProperties; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
|
||||
|
@Component |
||||
|
@ConfigurationProperties(prefix = "password-party") |
||||
|
public class PasswordPartyConfig { |
||||
|
|
||||
|
private String baseUrl; |
||||
|
|
||||
|
public String getBaseUrl() { |
||||
|
return baseUrl; |
||||
|
} |
||||
|
|
||||
|
public void setBaseUrl(String baseUrl) { |
||||
|
this.baseUrl = baseUrl; |
||||
|
} |
||||
|
} |
||||
@ -1,9 +1,8 @@ |
|||||
package com.zdxt.enforcementcode.domain.bean; |
package org.dromara.system.domain.bean; |
||||
|
|
||||
import org.dromara.common.mybatis.core.domain.BaseEntity; |
import com.baomidou.mybatisplus.annotation.TableId; |
||||
import com.baomidou.mybatisplus.annotation.*; |
import com.baomidou.mybatisplus.annotation.TableName; |
||||
import lombok.Data; |
import lombok.Data; |
||||
import lombok.EqualsAndHashCode; |
|
||||
|
|
||||
import java.io.Serial; |
import java.io.Serial; |
||||
import java.util.Date; |
import java.util.Date; |
||||
@ -1,9 +1,8 @@ |
|||||
package com.zdxt.enforcementcode.domain.bean; |
package org.dromara.system.domain.bean; |
||||
|
|
||||
import org.dromara.common.mybatis.core.domain.BaseEntity; |
import com.baomidou.mybatisplus.annotation.TableId; |
||||
import com.baomidou.mybatisplus.annotation.*; |
import com.baomidou.mybatisplus.annotation.TableName; |
||||
import lombok.Data; |
import lombok.Data; |
||||
import lombok.EqualsAndHashCode; |
|
||||
|
|
||||
import java.io.Serial; |
import java.io.Serial; |
||||
import java.util.Date; |
import java.util.Date; |
||||
@ -1,9 +1,8 @@ |
|||||
package com.zdxt.enforcementcode.domain.bean; |
package org.dromara.system.domain.bean; |
||||
|
|
||||
import org.dromara.common.mybatis.core.domain.BaseEntity; |
import com.baomidou.mybatisplus.annotation.TableId; |
||||
import com.baomidou.mybatisplus.annotation.*; |
import com.baomidou.mybatisplus.annotation.TableName; |
||||
import lombok.Data; |
import lombok.Data; |
||||
import lombok.EqualsAndHashCode; |
|
||||
|
|
||||
import java.io.Serial; |
import java.io.Serial; |
||||
|
|
||||
@ -1,10 +1,10 @@ |
|||||
package com.zdxt.enforcementcode.domain.vo; |
package org.dromara.system.domain.vo; |
||||
|
|
||||
import cn.idev.excel.annotation.ExcelIgnoreUnannotated; |
import cn.idev.excel.annotation.ExcelIgnoreUnannotated; |
||||
import cn.idev.excel.annotation.ExcelProperty; |
import cn.idev.excel.annotation.ExcelProperty; |
||||
import com.zdxt.enforcementcode.domain.bean.CryptoIntegrityData; |
|
||||
import io.github.linpeilie.annotations.AutoMapper; |
import io.github.linpeilie.annotations.AutoMapper; |
||||
import lombok.Data; |
import lombok.Data; |
||||
|
import org.dromara.system.domain.bean.CryptoIntegrityData; |
||||
|
|
||||
import java.io.Serial; |
import java.io.Serial; |
||||
import java.io.Serializable; |
import java.io.Serializable; |
||||
@ -1,10 +1,11 @@ |
|||||
package com.zdxt.enforcementcode.domain.vo; |
package org.dromara.system.domain.vo; |
||||
|
|
||||
import cn.idev.excel.annotation.ExcelIgnoreUnannotated; |
import cn.idev.excel.annotation.ExcelIgnoreUnannotated; |
||||
import cn.idev.excel.annotation.ExcelProperty; |
import cn.idev.excel.annotation.ExcelProperty; |
||||
import com.zdxt.enforcementcode.domain.bean.CryptoOperationAudit; |
|
||||
import io.github.linpeilie.annotations.AutoMapper; |
import io.github.linpeilie.annotations.AutoMapper; |
||||
import lombok.Data; |
import lombok.Data; |
||||
|
import org.dromara.system.domain.bean.CryptoOperationAudit; |
||||
|
|
||||
import java.io.Serial; |
import java.io.Serial; |
||||
import java.io.Serializable; |
import java.io.Serializable; |
||||
@ -1,11 +1,11 @@ |
|||||
package com.zdxt.enforcementcode.domain.vo; |
package org.dromara.system.domain.vo; |
||||
|
|
||||
|
|
||||
import cn.idev.excel.annotation.ExcelIgnoreUnannotated; |
import cn.idev.excel.annotation.ExcelIgnoreUnannotated; |
||||
import cn.idev.excel.annotation.ExcelProperty; |
import cn.idev.excel.annotation.ExcelProperty; |
||||
import com.zdxt.enforcementcode.domain.bean.CryptoSensitiveData; |
|
||||
import io.github.linpeilie.annotations.AutoMapper; |
import io.github.linpeilie.annotations.AutoMapper; |
||||
import lombok.Data; |
import lombok.Data; |
||||
|
import org.dromara.system.domain.bean.CryptoSensitiveData; |
||||
|
|
||||
import java.io.Serial; |
import java.io.Serial; |
||||
import java.io.Serializable; |
import java.io.Serializable; |
||||
@ -1,4 +1,4 @@ |
|||||
package com.zdxt.enforcementcode.domain.vo.passwordparty; |
package org.dromara.system.domain.vo; |
||||
|
|
||||
import lombok.Data; |
import lombok.Data; |
||||
|
|
||||
@ -1,9 +1,8 @@ |
|||||
package com.zdxt.enforcementcode.mapper; |
package org.dromara.system.mapper; |
||||
|
|
||||
|
|
||||
import com.zdxt.enforcementcode.domain.bean.CryptoIntegrityData; |
|
||||
import com.zdxt.enforcementcode.domain.vo.CryptoIntegrityDataVo; |
|
||||
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; |
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; |
||||
|
import org.dromara.system.domain.bean.CryptoIntegrityData; |
||||
|
import org.dromara.system.domain.vo.CryptoIntegrityDataVo; |
||||
|
|
||||
public interface CryptoIntegrityDataMapper extends BaseMapperPlus<CryptoIntegrityData, CryptoIntegrityDataVo> { |
public interface CryptoIntegrityDataMapper extends BaseMapperPlus<CryptoIntegrityData, CryptoIntegrityDataVo> { |
||||
|
|
||||
@ -1,9 +1,9 @@ |
|||||
package com.zdxt.enforcementcode.mapper; |
package org.dromara.system.mapper; |
||||
|
|
||||
|
|
||||
import com.zdxt.enforcementcode.domain.bean.CryptoOperationAudit; |
|
||||
import com.zdxt.enforcementcode.domain.vo.CryptoOperationAuditVo; |
|
||||
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; |
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; |
||||
|
import org.dromara.system.domain.bean.CryptoOperationAudit; |
||||
|
import org.dromara.system.domain.vo.CryptoOperationAuditVo; |
||||
|
|
||||
public interface CryptoOperationAuditMapper extends BaseMapperPlus<CryptoOperationAudit, CryptoOperationAuditVo> { |
public interface CryptoOperationAuditMapper extends BaseMapperPlus<CryptoOperationAudit, CryptoOperationAuditVo> { |
||||
|
|
||||
@ -1,8 +1,9 @@ |
|||||
package com.zdxt.enforcementcode.mapper; |
package org.dromara.system.mapper; |
||||
|
|
||||
|
|
||||
import com.zdxt.enforcementcode.domain.bean.CryptoSensitiveData; |
|
||||
import com.zdxt.enforcementcode.domain.vo.CryptoSensitiveDataVo; |
|
||||
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; |
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; |
||||
|
import org.dromara.system.domain.bean.CryptoSensitiveData; |
||||
|
import org.dromara.system.domain.vo.CryptoSensitiveDataVo; |
||||
|
|
||||
public interface CryptoSensitiveDataMapper extends BaseMapperPlus<CryptoSensitiveData, CryptoSensitiveDataVo> { |
public interface CryptoSensitiveDataMapper extends BaseMapperPlus<CryptoSensitiveData, CryptoSensitiveDataVo> { |
||||
|
|
||||
@ -0,0 +1,67 @@ |
|||||
|
package org.dromara.system.service; |
||||
|
|
||||
|
/** |
||||
|
* 密评敏感字段 HSM 加密服务 |
||||
|
* <p> |
||||
|
* 通过 HSM 密码服务平台对敏感字段进行加密存储,满足密评合规要求。 |
||||
|
* 手机号/身份证号使用 SM4-CBC 对称加密,密码使用 HMAC-SM3 单向哈希。 |
||||
|
*/ |
||||
|
public interface ICryptoSensitiveHsmService { |
||||
|
|
||||
|
/** |
||||
|
* 加密并存储敏感字段(SM4-CBC 对称加密) |
||||
|
* |
||||
|
* @param tableName 业务表名,如 sys_user |
||||
|
* @param bizId 业务记录主键 |
||||
|
* @param fieldCode 字段编码,如 phone / id_card |
||||
|
* @param plainText 明文值 |
||||
|
* @return HSM 加密后的密文(cipherText),加密失败或明文为空时返回 null |
||||
|
*/ |
||||
|
String encryptAndSave(String tableName, String bizId, String fieldCode, String plainText); |
||||
|
|
||||
|
/** |
||||
|
* 哈希并存储密码(HMAC-SM3 单向哈希) |
||||
|
* <p> |
||||
|
* 生成随机 salt,调用 HSM /hsm/api/HmacWithIndex 接口计算哈希, |
||||
|
* 哈希值存入 plain_hash,salt 存入 field_salt,cipher_text 为空。 |
||||
|
* |
||||
|
* @param tableName 业务表名 |
||||
|
* @param bizId 业务记录主键 |
||||
|
* @param plainPassword 明文密码 |
||||
|
* @return 生成的盐值(Hex),供登录校验时使用 |
||||
|
*/ |
||||
|
String hashAndSavePassword(String tableName, String bizId, String plainPassword); |
||||
|
|
||||
|
/** |
||||
|
* 验证密码是否匹配 |
||||
|
* |
||||
|
* @param tableName 业务表名 |
||||
|
* @param bizId 业务记录主键 |
||||
|
* @param plainPassword 明文密码 |
||||
|
* @return true=匹配 |
||||
|
*/ |
||||
|
boolean verifyPassword(String tableName, String bizId, String plainPassword); |
||||
|
|
||||
|
/** |
||||
|
* 解密获取明文(SM4-CBC 对称解密) |
||||
|
* |
||||
|
* @param tableName 业务表名 |
||||
|
* @param bizId 业务记录主键 |
||||
|
* @param fieldCode 字段编码 |
||||
|
* @return 明文值,不存在则返回 null |
||||
|
*/ |
||||
|
String decryptField(String tableName, String bizId, String fieldCode); |
||||
|
|
||||
|
/** |
||||
|
* 计算并存储数据完整性摘要(HMAC-SM3) |
||||
|
* <p> |
||||
|
* 将多个业务字段明文拼接后,调用 HSM HMAC-SM3 计算摘要, |
||||
|
* 结果存入 crypto_integrity_data 表,用于后续完整性校验。 |
||||
|
* |
||||
|
* @param tableName 业务表名,如 sys_user |
||||
|
* @param bizId 业务记录主键 |
||||
|
* @param fieldList 参与摘要的字段列表,逗号分隔,如 password,phonenumber,certificateNumber |
||||
|
* @param plainValues 与 fieldList 顺序一致的明文值 |
||||
|
*/ |
||||
|
void saveIntegrityHash(String tableName, String bizId, String fieldList, String... plainValues); |
||||
|
} |
||||
@ -0,0 +1,195 @@ |
|||||
|
package org.dromara.system.service.impl; |
||||
|
|
||||
|
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 lombok.RequiredArgsConstructor; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.dromara.common.satoken.utils.LoginHelper; |
||||
|
import org.dromara.system.domain.bean.CryptoIntegrityData; |
||||
|
import org.dromara.system.domain.bean.CryptoOperationAudit; |
||||
|
import org.dromara.system.domain.bean.CryptoSensitiveData; |
||||
|
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.service.ICryptoSensitiveHsmService; |
||||
|
import org.dromara.system.service.IPasswordPartyService; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
import org.springframework.transaction.annotation.Transactional; |
||||
|
|
||||
|
import java.nio.charset.StandardCharsets; |
||||
|
import java.util.Date; |
||||
|
|
||||
|
@Slf4j |
||||
|
@RequiredArgsConstructor |
||||
|
@Service |
||||
|
public class CryptoSensitiveHsmServiceImpl implements ICryptoSensitiveHsmService { |
||||
|
|
||||
|
private final IPasswordPartyService passwordPartyService; |
||||
|
private final CryptoSensitiveDataMapper cryptoSensitiveDataMapper; |
||||
|
private final CryptoOperationAuditMapper cryptoOperationAuditMapper; |
||||
|
private final CryptoIntegrityDataMapper cryptoIntegrityDataMapper; |
||||
|
|
||||
|
private static final String KEY_VERSION = "V1"; |
||||
|
private static final String TABLE_SYS_USER = "sys_user"; |
||||
|
|
||||
|
@Override |
||||
|
@Transactional(rollbackFor = Exception.class) |
||||
|
public String encryptAndSave(String tableName, String bizId, String fieldCode, String plainText) { |
||||
|
if (StrUtil.isBlank(plainText)) { |
||||
|
return null; |
||||
|
} |
||||
|
String inputHex = HexUtil.encodeHexStr(plainText.getBytes(StandardCharsets.UTF_8)); |
||||
|
SymmetryEncryptVo encryptResult = passwordPartyService.symmetryEncrypt(inputHex); |
||||
|
|
||||
|
CryptoSensitiveData existing = queryActiveRecord(tableName, bizId, fieldCode); |
||||
|
if (existing != null) { |
||||
|
existing.setCipherText(encryptResult.getOutData()); |
||||
|
existing.setFieldIv(encryptResult.getIvData()); |
||||
|
existing.setFieldValue(plainText); |
||||
|
existing.setEncryptAlg("SM4-CBC"); |
||||
|
existing.setKeyVersion(KEY_VERSION); |
||||
|
cryptoSensitiveDataMapper.updateById(existing); |
||||
|
saveAuditRecord(tableName, bizId, fieldCode, existing.getId(), 3, "HSM SM4-CBC 加密更新"); |
||||
|
} else { |
||||
|
CryptoSensitiveData record = new CryptoSensitiveData(); |
||||
|
record.setTableName(tableName); |
||||
|
record.setBizId(bizId); |
||||
|
record.setFieldCode(fieldCode); |
||||
|
record.setFieldValue(plainText); |
||||
|
record.setCipherText(encryptResult.getOutData()); |
||||
|
record.setFieldIv(encryptResult.getIvData()); |
||||
|
record.setEncryptAlg("SM4-CBC"); |
||||
|
record.setKeyVersion(KEY_VERSION); |
||||
|
record.setIsActive(1); |
||||
|
record.setIsDeleted(0); |
||||
|
cryptoSensitiveDataMapper.insert(record); |
||||
|
saveAuditRecord(tableName, bizId, fieldCode, record.getId(), 1, "HSM SM4-CBC 加密存储"); |
||||
|
} |
||||
|
return encryptResult.getOutData(); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
@Transactional(rollbackFor = Exception.class) |
||||
|
public String hashAndSavePassword(String tableName, String bizId, String plainPassword) { |
||||
|
if (StrUtil.isBlank(plainPassword)) { |
||||
|
return null; |
||||
|
} |
||||
|
return encryptAndSave(tableName, bizId, "password", plainPassword); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public boolean verifyPassword(String tableName, String bizId, String plainPassword) { |
||||
|
CryptoSensitiveData record = queryActiveRecord(tableName, bizId, "password"); |
||||
|
if (record == null || StrUtil.isBlank(record.getCipherText()) || StrUtil.isBlank(record.getFieldIv())) { |
||||
|
return false; |
||||
|
} |
||||
|
try { |
||||
|
SymmetryEncryptVo decryptResult = passwordPartyService.symmetryDecrypt(record.getFieldIv(), record.getCipherText()); |
||||
|
byte[] plainBytes = hexToBytes(decryptResult.getOutData()); |
||||
|
String decryptedPassword = new String(plainBytes, StandardCharsets.UTF_8); |
||||
|
return decryptedPassword.equals(plainPassword); |
||||
|
} catch (Exception e) { |
||||
|
log.error("HSM解密密码验证失败, tableName={}, bizId={}", tableName, bizId, e); |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public String decryptField(String tableName, String bizId, String fieldCode) { |
||||
|
CryptoSensitiveData record = queryActiveRecord(tableName, bizId, fieldCode); |
||||
|
if (record == null || StrUtil.isBlank(record.getCipherText())) { |
||||
|
return null; |
||||
|
} |
||||
|
SymmetryEncryptVo decryptResult = passwordPartyService.symmetryDecrypt(record.getFieldIv(), record.getCipherText()); |
||||
|
byte[] plainBytes = hexToBytes(decryptResult.getOutData()); |
||||
|
return new String(plainBytes, StandardCharsets.UTF_8); |
||||
|
} |
||||
|
|
||||
|
@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] : ""); |
||||
|
} |
||||
|
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); |
||||
|
|
||||
|
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); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private CryptoSensitiveData queryActiveRecord(String tableName, String bizId, String fieldCode) { |
||||
|
LambdaQueryWrapper<CryptoSensitiveData> lqw = new LambdaQueryWrapper<>(); |
||||
|
lqw.eq(CryptoSensitiveData::getTableName, tableName) |
||||
|
.eq(CryptoSensitiveData::getBizId, bizId) |
||||
|
.eq(CryptoSensitiveData::getFieldCode, fieldCode) |
||||
|
.eq(CryptoSensitiveData::getIsActive, 1) |
||||
|
.eq(CryptoSensitiveData::getIsDeleted, 0); |
||||
|
return cryptoSensitiveDataMapper.selectOne(lqw); |
||||
|
} |
||||
|
|
||||
|
private void deactivateExisting(String tableName, String bizId, String fieldCode) { |
||||
|
LambdaUpdateWrapper<CryptoSensitiveData> uw = new LambdaUpdateWrapper<>(); |
||||
|
uw.eq(CryptoSensitiveData::getTableName, tableName) |
||||
|
.eq(CryptoSensitiveData::getBizId, bizId) |
||||
|
.eq(CryptoSensitiveData::getFieldCode, fieldCode) |
||||
|
.eq(CryptoSensitiveData::getIsActive, 1) |
||||
|
.set(CryptoSensitiveData::getIsActive, 0); |
||||
|
cryptoSensitiveDataMapper.update(null, uw); |
||||
|
} |
||||
|
|
||||
|
private void saveAuditRecord(String tableName, String bizId, String fieldCode, |
||||
|
Long sensitiveDataId, int operateType, String remark) { |
||||
|
CryptoOperationAudit audit = new CryptoOperationAudit(); |
||||
|
audit.setSensitiveDataId(sensitiveDataId); |
||||
|
audit.setTableName(tableName); |
||||
|
audit.setBizId(bizId); |
||||
|
audit.setFieldCode(fieldCode); |
||||
|
audit.setOperateType(operateType); |
||||
|
audit.setRemark(remark); |
||||
|
try { |
||||
|
audit.setOperatorId(LoginHelper.getUserId()); |
||||
|
} catch (Exception ignored) { |
||||
|
} |
||||
|
cryptoOperationAuditMapper.insert(audit); |
||||
|
} |
||||
|
|
||||
|
private static byte[] hexToBytes(String hex) { |
||||
|
int len = hex.length(); |
||||
|
byte[] data = new byte[len / 2]; |
||||
|
for (int i = 0; i < len; i += 2) { |
||||
|
data[i / 2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4) |
||||
|
+ Character.digit(hex.charAt(i + 1), 16)); |
||||
|
} |
||||
|
return data; |
||||
|
} |
||||
|
} |
||||
@ -1,13 +1,14 @@ |
|||||
package com.zdxt.enforcementcode.service.thirdparty.impl; |
package org.dromara.system.service.impl; |
||||
|
|
||||
import cn.hutool.http.HttpRequest; |
import cn.hutool.http.HttpRequest; |
||||
import cn.hutool.http.HttpResponse; |
import cn.hutool.http.HttpResponse; |
||||
import com.alibaba.fastjson.JSONObject; |
import com.alibaba.fastjson.JSONObject; |
||||
import com.zdxt.enforcementcode.domain.vo.passwordparty.SymmetryEncryptVo; |
|
||||
import com.zdxt.enforcementcode.service.thirdparty.IPasswordPartyService; |
|
||||
import lombok.RequiredArgsConstructor; |
import lombok.RequiredArgsConstructor; |
||||
import lombok.extern.slf4j.Slf4j; |
import lombok.extern.slf4j.Slf4j; |
||||
import org.dromara.common.core.exception.base.BaseException; |
import org.dromara.common.core.exception.base.BaseException; |
||||
|
import org.dromara.system.config.PasswordPartyConfig; |
||||
|
import org.dromara.system.domain.vo.SymmetryEncryptVo; |
||||
|
import org.dromara.system.service.IPasswordPartyService; |
||||
import org.springframework.stereotype.Service; |
import org.springframework.stereotype.Service; |
||||
|
|
||||
/** |
/** |
||||
Loading…
Reference in new issue