Compare commits

..

3 Commits

Author SHA1 Message Date
唐潇凯
7a995e159f 密钥存储修改 2025-07-03 09:53:16 +08:00
唐潇凯
4569db66ad Merge remote-tracking branch 'origin/jenkins-engin' into jenkins-engin
# Conflicts:
#	modules/module-ci-machine/src/main/java/cd/casic/module/machine/service/impl/SecretKeyServiceImpl.java
2025-07-03 09:37:34 +08:00
唐潇凯
d4a3b9b1b9 密钥存储修改 2025-07-03 09:30:38 +08:00
7 changed files with 44 additions and 141 deletions

View File

@ -99,9 +99,9 @@ public class SecretKeyController {
return success(BeanUtils.toBean(pageResult, SecretKeyVO.class)); return success(BeanUtils.toBean(pageResult, SecretKeyVO.class));
} }
@GetMapping("/download") // @GetMapping("/download")
@Operation(summary = "下载密钥文件") // @Operation(summary = "下载密钥文件")
public ResponseEntity<InputStreamResource> downloadSecretFile(@RequestParam("id") Long id) { // public ResponseEntity<InputStreamResource> downloadSecretFile(@RequestParam("id") Long id) {
return secretKeyService.downloadSecretFile(id); // return secretKeyService.downloadSecretFile(id);
} // }
} }

View File

@ -27,12 +27,6 @@ public class SecretKeyVO extends PageParam {
@Schema(description = "密钥描述", example = "用于加密敏感数据的密钥") @Schema(description = "密钥描述", example = "用于加密敏感数据的密钥")
private String description; private String description;
@Schema(description = "存储路径(本地上传文件路径)", example = "/data/secret_keys/")
private String path;
@Schema(description = "文件名", example = "key.pem")
private String fileName;
@Schema(description = "密钥密码", example = "******") @Schema(description = "密钥密码", example = "******")
private String password; private String password;
@ -44,4 +38,11 @@ public class SecretKeyVO extends PageParam {
@Schema(description = "关联的机器ID列表", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1024, 2048]") @Schema(description = "关联的机器ID列表", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1024, 2048]")
private List<Long> machineInfoIds; private List<Long> machineInfoIds;
@Schema(description = "私钥", requiredMode = Schema.RequiredMode.REQUIRED, example = "******")
private String private_key;
@Schema(description = "公钥", requiredMode = Schema.RequiredMode.REQUIRED, example = "******")
private String public_key;
} }

View File

@ -28,14 +28,17 @@ public class SecretKeyDO extends BaseDO {
@TableField(value = "description") @TableField(value = "description")
private String description; private String description;
//oss存储路径
@TableField(value = "path")
private String path;
@TableField
private String fileName;
//密钥密码 //密钥密码
@TableField(value = "password") @TableField(value = "password")
private String password; private String password;
@TableField(value = "public_key")
private String public_key;
@TableField(value = "private_key")
private String private_key;
} }

View File

@ -32,6 +32,13 @@ public interface MachineInfoService {
void deleteMachineInfo(Long machineInfoId); void deleteMachineInfo(Long machineInfoId);
/**
* 获取绑定的密钥
* @param secretKeyId 密钥id
* @return 绑定的机器列表
*/
List<MachineInfoDO>selectBindMachineBySecretKey(Long secretKeyId);
/** /**
* 测试机器连接 * 测试机器连接

View File

@ -27,10 +27,8 @@ public interface SecretKeyService {
void unbindMachine(@Valid SecretKeyVO secretKeyVO); void unbindMachine(@Valid SecretKeyVO secretKeyVO);
ResponseEntity<InputStreamResource> downloadSecretFile(Long id);
List<MachineInfoDO> getBindMachine(Long secretKeyId); List<MachineInfoDO> getBindMachine(Long secretKeyId);
String getKeyContent(Long secretKeyId); // String getKeyContent(Long secretKeyId);
} }

View File

@ -107,6 +107,11 @@ public class MachineInfoServiceImpl implements MachineInfoService {
machineInfoMapper.deleteById(machineInfoId); machineInfoMapper.deleteById(machineInfoId);
} }
@Override
public List<MachineInfoDO> selectBindMachineBySecretKey(Long secretKeyId) {
return machineInfoMapper.selectBindMachineBySecretKey(secretKeyId);
}
@Override @Override
public boolean testConnection(Long id) { public boolean testConnection(Long id) {

View File

@ -5,7 +5,6 @@ import cd.casic.framework.commons.util.object.BeanUtils;
import cd.casic.module.machine.controller.vo.SecretKeyVO; import cd.casic.module.machine.controller.vo.SecretKeyVO;
import cd.casic.module.machine.dal.dataobject.MachineInfoDO; import cd.casic.module.machine.dal.dataobject.MachineInfoDO;
import cd.casic.module.machine.dal.dataobject.SecretKeyDO; import cd.casic.module.machine.dal.dataobject.SecretKeyDO;
import cd.casic.module.machine.dal.mysql.MachineInfoMapper;
import cd.casic.module.machine.dal.mysql.SecretKeyMapper; import cd.casic.module.machine.dal.mysql.SecretKeyMapper;
import cd.casic.module.machine.service.MachineInfoService; import cd.casic.module.machine.service.MachineInfoService;
import cn.hutool.core.io.resource.ResourceUtil; import cn.hutool.core.io.resource.ResourceUtil;
@ -14,18 +13,9 @@ import cd.casic.module.machine.utils.AliYunOssClient;
import cd.casic.module.machine.service.SecretKeyService; import cd.casic.module.machine.service.SecretKeyService;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StreamUtils;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.List; import java.util.List;
import static cd.casic.framework.commons.exception.util.ServiceExceptionUtil.exception; import static cd.casic.framework.commons.exception.util.ServiceExceptionUtil.exception;
@ -34,28 +24,15 @@ import static cd.casic.module.machine.contants.MachineErrorCodeConstants.*;
/** /**
* 密钥服务实现类 * 密钥服务实现类
*/ */
@Slf4j
@Service("secretKeyService") @Service("secretKeyService")
public class SecretKeyServiceImpl implements SecretKeyService { public class SecretKeyServiceImpl implements SecretKeyService {
@Resource @Resource
private MachineInfoService machineInfoService; private MachineInfoService machineInfoService;
@Resource
private AliYunOssClient aliYunOssClient;
@Resource @Resource
private SecretKeyMapper secretKeyMapper; private SecretKeyMapper secretKeyMapper;
@Resource
private MachineInfoMapper machineInfoMapper;
@Override
public SecretKeyVO getSecretKey(Long id) {
SecretKeyDO secretKeyDO = validateSecretKeyExists(id);
return BeanUtils.toBean(secretKeyDO, SecretKeyVO.class);
}
@Override @Override
public void deleteSecretKey(Long id) { public void deleteSecretKey(Long id) {
validateSecretKeyExists(id); validateSecretKeyExists(id);
@ -63,61 +40,35 @@ public class SecretKeyServiceImpl implements SecretKeyService {
} }
@Override @Override
public void unbindMachine(SecretKeyVO secretKeyVO) { public SecretKeyVO getSecretKey(Long id) {
machineInfoService.bindingSecretKey(secretKeyVO.getMachineInfoIds(), null); SecretKeyDO secretKeyDO = validateSecretKeyExists(id);
return BeanUtils.toBean(secretKeyDO, SecretKeyVO.class);
} }
@Override @Override
public ResponseEntity<InputStreamResource> downloadSecretFile(Long id) { public void unbindMachine(SecretKeyVO secretKeyVO) {
// 获取私钥内容 machineInfoService.bindingSecretKey(secretKeyVO.getMachineInfoIds(),null);
String keyContent = getKeyContent(id);
if (keyContent == null || keyContent.isEmpty()) {
throw exception(SECRET_KEY_NULL);
}
// 将字符串转换为输入流
ByteArrayInputStream inputStream = new ByteArrayInputStream(keyContent.getBytes(StandardCharsets.UTF_8));
// 设置响应头
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=private_key.pem");
headers.add(HttpHeaders.CONTENT_TYPE, "application/octet-stream");
headers.add(HttpHeaders.CONTENT_LENGTH, String.valueOf(keyContent.getBytes(StandardCharsets.UTF_8).length));
// 返回带有文件流的响应实体
return ResponseEntity.ok()
.headers(headers)
.body(new InputStreamResource(inputStream));
} }
@Override @Override
public List<MachineInfoDO> getBindMachine(Long secretKeyId) { public List<MachineInfoDO> getBindMachine(Long secretKeyId) {
return machineInfoMapper.selectBindMachineBySecretKey(secretKeyId); return machineInfoService.selectBindMachineBySecretKey(secretKeyId);
} }
@Override @Override
public Long createSecretKey(SecretKeyVO secretKeyVO) { public Long createSecretKey(SecretKeyVO secretKeyVO) {
validateSecretKeyAdd(secretKeyVO); validateSecretKeyAdd(secretKeyVO);
String localPath = secretKeyVO.getPath();
//将反斜杠替换为双反斜杠Windows路径转义
localPath = localPath.replace("\\", "\\\\");
String ossPath = upLoadSecretKey(localPath);
//检查得到的oss路径是否为空
validateSecretKeyPath(ossPath);
secretKeyVO.setPath(ossPath);
SecretKeyDO secretKeyDO = BeanUtils.toBean(secretKeyVO, SecretKeyDO.class); SecretKeyDO secretKeyDO = BeanUtils.toBean(secretKeyVO, SecretKeyDO.class);
secretKeyMapper.insert(secretKeyDO); secretKeyMapper.insert(secretKeyDO);
return secretKeyDO.getId(); return secretKeyDO.getId();
} }
@Override @Override
public void updateSecretKey(SecretKeyVO secretKeyVO) { public void updateSecretKey(SecretKeyVO secretKeyVO) {
SecretKeyDO secretKeyDO = validateSecretKeyExists(secretKeyVO.getId()); SecretKeyDO secretKeyDO = validateSecretKeyExists(secretKeyVO.getId());
//路径改变==改变密钥 BeanUtils.copyProperties(secretKeyVO, secretKeyDO);
if (!secretKeyDO.getPath().equals(secretKeyVO.getPath())) {
String ossPath = upLoadSecretKey(secretKeyVO.getPath());
BeanUtils.copyProperties(secretKeyVO, secretKeyDO);
secretKeyDO.setPath(ossPath);
} else {
BeanUtils.copyProperties(secretKeyVO, secretKeyDO);
}
secretKeyMapper.updateById(secretKeyDO); secretKeyMapper.updateById(secretKeyDO);
} }
@ -130,24 +81,7 @@ public class SecretKeyServiceImpl implements SecretKeyService {
@Override @Override
@Transactional @Transactional
public void deleteSecretKeyList(List<Long> ids) { public void deleteSecretKeyList(List<Long> ids) {
ids.forEach( machineInfoService.bindingSecretKey(ids,null);
secretKeyId -> {
SecretKeyDO secretKeyDO = validateSecretKeyExists(secretKeyId);
if (secretKeyDO.getPath() != null && !secretKeyDO.getPath().isEmpty()) {
try {
//文件名
//删除子目录文件需要在前面加上根目录文件路径
String fileName = secretKeyDO.getPath().substring(secretKeyDO.getPath().lastIndexOf("/") + 1);
aliYunOssClient.delete(fileName);
} catch (Exception e) {
throw exception(DELETE_FILE_FAIL);
}
}
}
);
//绑定的机器全部设置为空
machineInfoService.bindingSecretKey(ids, null);
secretKeyMapper.deleteBatchIds(ids); secretKeyMapper.deleteBatchIds(ids);
} }
@ -156,38 +90,14 @@ public class SecretKeyServiceImpl implements SecretKeyService {
return secretKeyMapper.selectPage(secretKeyVO); return secretKeyMapper.selectPage(secretKeyVO);
} }
public String upLoadSecretKey(String localPath) {
//使用S3FileClient上传文件
aliYunOssClient.init();
//传输到指定文件需要在path前面加上文件路径
String path = IdUtil.fastSimpleUUID() + ".txt";
//上传文件是从本地上传这里传的是本地文件地址
byte[] content = ResourceUtil.readBytes(localPath);
String ossPath;
try {
ossPath = aliYunOssClient.upload(content, path, "txt");
} catch (Exception e) {
throw exception(UPLOADING_FILE_FAIL);
}
return ossPath;
}
@VisibleForTesting @VisibleForTesting
void validateSecretKeyAdd(SecretKeyVO secretKeyVO) { void validateSecretKeyAdd(SecretKeyVO secretKeyVO) {
if (secretKeyVO == null) { if (secretKeyVO == null) {
throw exception(SECRET_KEY_NULL); throw exception(SECRET_KEY_NULL);
} }
if (secretKeyVO.getPath().isEmpty()) {
throw exception(SECRET_KEY_PATH_NULL);
}
} }
@VisibleForTesting
void validateSecretKeyPath(String path) {
if (path.isEmpty()) {
throw exception(SECRET_KEY_PATH_NULL);
}
}
@VisibleForTesting @VisibleForTesting
@ -202,25 +112,4 @@ public class SecretKeyServiceImpl implements SecretKeyService {
return secretKeyDO; return secretKeyDO;
} }
@Override
public String getKeyContent(Long secretKeyId) {
if (secretKeyId == null) {
return null;
}
SecretKeyVO secretKey = getSecretKey(secretKeyId);
byte[] content;
try {
content = aliYunOssClient.getContent(secretKey.getPath().substring(secretKey.getPath().lastIndexOf("/") + 1));
} catch (Exception e) {
log.error("读取密钥文件失败", e);
throw exception(READ_SECRET_CONTENT_ERROR);
}
//改为S3FileClient读取
InputStream read = new ByteArrayInputStream(content);
try {
return StreamUtils.copyToString(read, StandardCharsets.UTF_8);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
} }