This commit is contained in:
even 2025-07-23 16:31:56 +08:00
commit 2f7f414679
13 changed files with 537 additions and 23 deletions

View File

@ -22,4 +22,17 @@ public enum AuthenticationType implements IntArrayValuable {
public int[] array() {
return ARRAYS;
}
public static AuthenticationType of(int code) {
if (code == 0) {
return null;
}
for (AuthenticationType value : values()) {
if (value.code==code) {
return value;
}
}
return null;
}
}

View File

@ -0,0 +1,88 @@
package cd.casic.ci.api;
import cd.casic.ci.process.process.service.sftpFile.SftpFileService;
import cd.casic.ci.process.properties.TargetFileUploadProperties;
import cd.casic.framework.commons.pojo.CommonResult;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.util.List;
/**
* @author HopeLi
* @version v1.0
* @ClassName SftpFileController
* @Date: 2025/7/1 9:03
* @Description:
*/
@RestController
@RequestMapping("/sftpFile")
public class SftpFileController {
@Resource
private SftpFileService sftpFileService;
@Resource
private TargetFileUploadProperties fileUploadProperties;
@PostMapping("/upload")
public CommonResult<String> uploadFile(
@RequestParam String remoteDir,
@RequestParam String remoteFileName,
@RequestParam MultipartFile file) {
String localFilePath = saveTempFile(file);
sftpFileService.uploadFile(
fileUploadProperties.getRemoteHost(),
fileUploadProperties.getRemotePort(),
fileUploadProperties.getUsername(),
fileUploadProperties.getPassword(),
fileUploadProperties.getSshKeyPath(),
localFilePath, remoteDir, remoteFileName);
return CommonResult.success("文件上传成功");
}
@GetMapping("/download")
public void downloadFile(
@RequestParam String remoteFilePath,
HttpServletResponse response) {
sftpFileService.downloadFile(
fileUploadProperties.getRemoteHost(),
fileUploadProperties.getRemotePort(),
fileUploadProperties.getUsername(),
fileUploadProperties.getPassword(),
fileUploadProperties.getSshKeyPath(),
remoteFilePath, response);
}
@PostMapping("/download/zip")
public void downloadFilesAsZip(
@RequestBody List<String> remoteFilePaths,
@RequestParam String zipFileName,
HttpServletResponse response) {
sftpFileService.downloadFilesAsZip(
fileUploadProperties.getRemoteHost(),
fileUploadProperties.getRemotePort(),
fileUploadProperties.getUsername(),
fileUploadProperties.getPassword(),
fileUploadProperties.getSshKeyPath(),
remoteFilePaths, zipFileName, response);
}
private String saveTempFile(MultipartFile file) {
try {
File tempFile = File.createTempFile("upload-", ".tmp");
file.transferTo(tempFile);
return tempFile.getAbsolutePath();
} catch (IOException e) {
throw new RuntimeException("保存临时文件失败", e);
}
}
}

View File

@ -0,0 +1,264 @@
package cd.casic.ci.process.process.service.sftpFile;
import cd.casic.ci.process.util.SftpUploadUtil;
import cd.casic.framework.commons.exception.ServiceException;
import com.amazonaws.util.IOUtils;
import com.jcraft.jsch.*;
import java.io.*;
import java.util.List;
import java.util.Vector;
/**
* @author HopeLi
* @version v1.0
* @ClassName SftpClientUtils
* @Date: 2025/7/22 15:09
* @Description:
*/
public class SftpClientUtils implements AutoCloseable {
private static final int DEFAULT_SFTP_PORT = 22;
private Session session;
private ChannelSftp channelSftp;
public SftpClientUtils(String remoteHost, Integer remotePort, String username,
String password, String sshKeyPath) throws JSchException, SftpException, SftpUploadUtil.SftpUploadException {
JSch jsch = new JSch();
// 1. 添加身份认证信息 (密码或密钥)
if (sshKeyPath != null && !sshKeyPath.trim().isEmpty()) {
// 使用 SSH Key 认证
File sshKeyFile = new File(sshKeyPath);
if (!sshKeyFile.exists() || !sshKeyFile.isFile()) {
throw new SftpUploadUtil.SftpUploadException("SSH Key 文件不存在或不是一个有效文件: " + sshKeyPath);
}
jsch.addIdentity(sshKeyPath);
System.out.println("使用 SSH Key 认证: " + sshKeyPath);
} else if (password == null || password.trim().isEmpty()) {
// 如果没有提供密码或密钥路径则认证信息不全
throw new SftpUploadUtil.SftpUploadException("必须提供密码或 SSH Key 路径进行 SFTP 认证.");
}
// 如果提供了密码将在 getSession 后设置因为 getSession 需要用户名主机和端口先建立连接意图
// 2. 获取 Session
int port = (remotePort != null && remotePort > 0) ? remotePort : DEFAULT_SFTP_PORT;
session = jsch.getSession(username, remoteHost, port);
System.out.println("尝试连接 SFTP 服务器: " + username + "@" + remoteHost + ":" + port);
// 如果使用密码认证且提供了密码
if (password != null && !password.trim().isEmpty() && (sshKeyPath == null || sshKeyPath.trim().isEmpty())) {
session.setPassword(password);
System.out.println("使用密码认证.");
}
// 设置连接不进行主机密钥检查 (生产环境不推荐应该配置 known_hosts)
// 在实际应用中应该引导用户信任主机密钥或提前将主机密钥加入 known_hosts
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no"); // !!! 生产环境请谨慎使用或配置正确的主机密钥检查 !!!
session.setConfig(config);
// 3. 连接 Session
session.connect();
System.out.println("SFTP Session 连接成功.");
// 4. 打开 SFTP Channel
Channel channel = session.openChannel("sftp");
channel.connect();
System.out.println("SFTP Channel 打开成功.");
channelSftp = (ChannelSftp) channel;
}
public void uploadFile(String localFilePath, String remoteDir, String remoteFileName) throws SftpException, SftpUploadUtil.SftpUploadException, FileNotFoundException {
FileInputStream fis = null;
// 检查并切换到远程目录
try {
channelSftp.cd(remoteDir);
System.out.println("已切换到远程目录: " + remoteDir);
} catch (SftpException e) {
// 如果远程目录不存在尝试创建
System.err.println("远程目录不存在: " + remoteDir + ",尝试创建...");
try {
// 尝试递归创建目录 (如果需要)
createRemoteDirRecursive(channelSftp, remoteDir);
channelSftp.cd(remoteDir); // 创建后再次切换
System.out.println("远程目录创建成功并已切换: " + remoteDir);
} catch (SftpException e2) {
// 创建目录失败
throw new SftpUploadUtil.SftpUploadException("远程目录创建失败: " + remoteDir, e2);
}
}
// 6. 获取本地文件流
File localFile = new File(localFilePath);
if (!localFile.exists() || !localFile.isFile()) {
throw new SftpUploadUtil.SftpUploadException("本地文件不存在或不是一个有效文件: " + localFilePath);
}
fis = new FileInputStream(localFile);
System.out.println("本地文件流获取成功: " + localFilePath);
// 7. 确定远程文件名
String finalRemoteFileName = (remoteFileName != null && !remoteFileName.trim().isEmpty()) ?
remoteFileName : localFile.getName();
System.out.println("最终上传到远程的文件名为: " + finalRemoteFileName);
// 8. 上传文件
channelSftp.put(fis, finalRemoteFileName);
System.out.println("文件上传成功!");
}
public void downloadFile(String remoteFilePath, OutputStream outputStream) throws SftpException, SftpUploadUtil.SftpUploadException, IOException {
InputStream inputStream = null;
String remoteDir = remoteFilePath.substring(0, remoteFilePath.lastIndexOf('/'));
String fileName = remoteFilePath.substring(remoteFilePath.lastIndexOf('/') + 1);
// 切换目录并列出内容用于调试
try {
channelSftp.cd(remoteDir);
} catch (SftpException e) {
throw new SftpUploadUtil.SftpUploadException("切换远程目录失败: " + remoteDir, e);
}
// 列出目录内容用于调试
Vector<ChannelSftp.LsEntry> entries = channelSftp.ls(".");
List<String> fileNames = entries.stream()
.map(entry -> entry.getFilename())
.filter(name -> !name.equals(".") && !name.equals(".."))
.toList();
System.out.println("远程目录中的文件列表: " + fileNames);
// 尝试获取文件流
try {
inputStream = channelSftp.get(fileName);
if (inputStream == null) {
throw new SftpUploadUtil.SftpUploadException("无法获取远程文件输入流,请检查文件是否存在或被其他进程占用: " + fileName);
}
IOUtils.copy(inputStream,outputStream);
outputStream.flush();
} catch (SftpException e) {
throw new SftpUploadUtil.SftpUploadException("文件下载失败: " + remoteFilePath, e);
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
System.err.println("关闭 InputStream 时发生异常: " + e.getMessage());
}
}
}
}
public InputStream downloadFileToStream(String remoteFilePath) throws SftpException, SftpUploadUtil.SftpUploadException {
InputStream inputStream = null;
String remoteDir = remoteFilePath.substring(0, remoteFilePath.lastIndexOf('/'));
String fileName = remoteFilePath.substring(remoteFilePath.lastIndexOf('/') + 1);
// 切换目录并列出内容用于调试
try {
channelSftp.cd(remoteDir);
} catch (SftpException e) {
throw new SftpUploadUtil.SftpUploadException("切换远程目录失败: " + remoteDir, e);
}
// 列出目录内容用于调试
Vector<ChannelSftp.LsEntry> entries = channelSftp.ls(".");
List<String> fileNames = entries.stream()
.map(entry -> entry.getFilename())
.filter(name -> !name.equals(".") && !name.equals(".."))
.toList();
System.out.println("远程目录中的文件列表: " + fileNames);
// 尝试获取文件流
try {
inputStream = channelSftp.get(fileName);
if (inputStream == null) {
throw new SftpUploadUtil.SftpUploadException("无法获取远程文件输入流,请检查文件是否存在或被其他进程占用: " + fileName);
}
return inputStream;
} catch (SftpException e) {
throw new SftpUploadUtil.SftpUploadException("获取远程文件流失败: " + fileName, e);
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
System.err.println("关闭 InputStream 时发生异常: " + e.getMessage());
}
}
}
}
/**
* 辅助方法递归创建远程目录
* @param channelSftp ChannelSftp 实例
* @param remoteDir 要创建的目录路径
* @throws SftpException 如果创建失败
*/
private static void createRemoteDirRecursive(ChannelSftp channelSftp, String remoteDir) throws SftpException {
// 标准化路径去掉末尾的 /
String cleanRemoteDir = remoteDir.endsWith("/") ? remoteDir.substring(0, remoteDir.length() - 1) : remoteDir;
String[] pathElements = cleanRemoteDir.split("/");
StringBuilder currentDir = new StringBuilder();
try {
channelSftp.cd("/"); // 先回到根目录
} catch (SftpException e) {
// 理论上不应该失败除非根目录都不可访问
throw new ServiceException();
}
for (String dir : pathElements) {
if (dir == null || dir.isEmpty()) {
continue; // 跳过空的路径元素比如路径以/开头
}
currentDir.append("/").append(dir);
try {
channelSftp.cd(currentDir.toString());
} catch (SftpException e) {
if (e.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) {
// 目录不存在创建它
try {
System.out.println("创建目录: " + currentDir.toString());
channelSftp.mkdir(currentDir.toString());
channelSftp.cd(currentDir.toString()); // 创建后进入该目录
System.out.println("目录创建成功并进入: " + currentDir.toString());
} catch (SftpException e2) {
throw new SftpException(e2.id, "无法创建远程目录: " + currentDir.toString(), e2);
}
} else {
// 其他 SFTP 异常
throw new SftpException(e.id, "切换或检查远程目录失败: " + currentDir.toString(), e);
}
}
}
}
@Override
public void close() {
if (channelSftp != null) {
channelSftp.disconnect();
}
if (session != null) {
session.disconnect();
}
}
}

View File

@ -0,0 +1,22 @@
package cd.casic.ci.process.process.service.sftpFile;
import jakarta.servlet.http.HttpServletResponse;
import java.util.List;
/**
* @author HopeLi
* @version v1.0
* @ClassName TargetManagerService
* @Date: 2025/5/17 10:20
* @Description:
*/
public interface SftpFileService{
void uploadFile(String remoteHost, Integer remotePort, String username, String password, String sshKeyPath, String localFilePath, String remoteDir, String remoteFileName);
void downloadFile(String remoteHost, Integer remotePort, String username, String password, String sshKeyPath, String remoteFilePath, HttpServletResponse response);
void downloadFilesAsZip(String remoteHost, Integer remotePort, String username, String password, String sshKeyPath, List<String> remoteFilePaths, String zipFileName, HttpServletResponse response);
}

View File

@ -0,0 +1,76 @@
package cd.casic.ci.process.process.service.sftpFile.impl;
import cd.casic.ci.process.process.service.sftpFile.SftpClientUtils;
import cd.casic.ci.process.process.service.sftpFile.SftpFileService;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
* @author HopeLi
* @version v1.0
* @ClassName SftpFileServiceImpl
* @Date: 2025/5/17 15:19
* @Description:
*/
@Service
@Slf4j
public class SftpFileServiceImpl implements SftpFileService {
@Override
public void uploadFile(String remoteHost, Integer remotePort, String username, String password, String sshKeyPath, String localFilePath, String remoteDir, String remoteFileName) {
try (SftpClientUtils client = new SftpClientUtils(remoteHost, remotePort, username, password, sshKeyPath)) {
client.uploadFile(localFilePath, remoteDir, remoteFileName);
} catch (Exception e) {
throw new RuntimeException("文件上传失败: " + e.getMessage(), e);
}
}
@Override
public void downloadFile(String remoteHost, Integer remotePort, String username, String password, String sshKeyPath, String remoteFilePath, HttpServletResponse response) {
try (SftpClientUtils client = new SftpClientUtils(remoteHost, remotePort, username, password, sshKeyPath)) {
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment; filename=\"" + new File(remoteFilePath).getName() + "\"");
try (OutputStream out = response.getOutputStream()) {
client.downloadFile(remoteFilePath, out);
}
} catch (Exception e) {
throw new RuntimeException("文件下载失败: " + e.getMessage(), e);
}
}
@Override
public void downloadFilesAsZip(String remoteHost, Integer remotePort, String username, String password, String sshKeyPath, List<String> remoteFilePaths, String zipFileName, HttpServletResponse response) {
try (SftpClientUtils client = new SftpClientUtils(remoteHost, remotePort, username, password, sshKeyPath)) {
response.setContentType("application/zip");
response.setHeader("Content-Disposition", "attachment; filename=\"" + zipFileName + "\"");
try (ZipOutputStream zipOut = new ZipOutputStream(response.getOutputStream())) {
for (String remoteFilePath : remoteFilePaths) {
String fileName = remoteFilePath.substring(remoteFilePath.lastIndexOf("/") + 1);
try (InputStream in = client.downloadFileToStream(remoteFilePath)) {
zipOut.putNextEntry(new ZipEntry(fileName));
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = in.read(buffer)) > 0) {
zipOut.write(buffer, 0, bytesRead);
}
zipOut.closeEntry();
}
}
}
} catch (Exception e) {
throw new RuntimeException("批量下载并打包 ZIP 失败: " + e.getMessage(), e);
}
}
}

View File

@ -6,11 +6,9 @@ import cd.casic.ci.process.dto.resp.target.TargetManagerResp;
import cd.casic.ci.process.dto.resp.target.TargetVersionResp;
import cd.casic.ci.process.process.converter.TargetConverter;
import cd.casic.ci.process.process.converter.TargetVersionConverter;
import cd.casic.ci.process.process.dao.pipeline.PipelineDao;
import cd.casic.ci.process.process.dao.pipeline.TargetManagerDao;
import cd.casic.ci.process.process.dao.pipeline.TargetVersionDao;
import cd.casic.ci.process.process.dataObject.base.BaseIdReq;
import cd.casic.ci.process.process.dataObject.pipeline.PipPipeline;
import cd.casic.ci.process.process.dataObject.target.TargetManager;
import cd.casic.ci.process.process.dataObject.target.TargetVersion;
import cd.casic.ci.process.process.service.pipeline.PipelineService;
@ -67,8 +65,6 @@ public class TargetManagerServiceImpl extends ServiceImpl<TargetManagerDao, Targ
private AdminUserServiceImpl adminUserService;
@Resource
private TargetFileUploadProperties fileUploadProperties;
@Resource
private PipelineDao pipelineDao;
@Resource

View File

@ -9,6 +9,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import static cd.casic.framework.commons.pojo.CommonResult.success;
@ -42,5 +43,12 @@ public class TerminalController {
return success(terminalService.getTerminalTransferToken());
}
@GetMapping("/test")
@Operation(summary = "测试主机连接")
// @PreAuthorize("@ss.hasPermission('asset:host:update')")
public CommonResult testHostConnect(@RequestParam Long id) {
return terminalService.testHostConnect(id);
}
}

View File

@ -64,22 +64,18 @@ public class TerminalConnectDTO {
@Schema(description = "用户名")
private String username;
// @Desensitize(toEmpty = true)
@Schema(description = "密码")
private String password;
@Schema(description = "密钥id")
private Long keyId;
// @Desensitize(toEmpty = true)
@Schema(description = "公钥文本")
private String publicKey;
// @Desensitize(toEmpty = true)
@Schema(description = "私钥文本")
private String privateKey;
// @Desensitize(toEmpty = true)
@Schema(description = "私钥密码")
private String privateKeyPassword;

View File

@ -73,13 +73,13 @@ public class SessionStores {
if (useKey) {
// 加载密钥
String publicKey = Optional.ofNullable(conn.getPublicKey())
.map(CryptogramUtil::doDecrypt)
.map(obj->CryptogramUtil.doDecrypt(obj))
.orElse(null);
String privateKey = Optional.ofNullable(conn.getPrivateKey())
.map(CryptogramUtil::doDecrypt)
.map(obj->CryptogramUtil.doDecrypt(obj))
.orElse(null);
String password = Optional.ofNullable(conn.getPrivateKeyPassword())
.map(CryptogramUtil::doDecrypt)
.map(obj->CryptogramUtil.doDecrypt(obj))
.orElse(null);
sessionHolder.addIdentityValue(String.valueOf(conn.getKeyId()),
privateKey,

View File

@ -33,9 +33,9 @@ import java.util.Map;
/**
* 终端连接检查
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023/12/29 15:32
* @author Yuru Pu
* @version 1.0
* @since 2025/7/21 14:17
*/
@Slf4j
@Component
@ -50,8 +50,6 @@ public class TerminalCheckHandler extends AbstractTerminalHandler<TerminalCheckR
@Resource
private TerminalConnectLogService terminalConnectLogService;
/*@Resource
private OperatorLogFrameworkService operatorLogFrameworkService;*/
@Override
public void handle(WebSocketSession channel, TerminalCheckRequest payload) {

View File

@ -13,6 +13,14 @@ import cd.casic.module.terminal.controller.dto.TerminalConnectDTO;
*/
public interface HostConnectService {
/**
* 获取 SSH 连接信息
*
* @param hostId hostId
* @return session
*/
TerminalConnectDTO getSshConnectInfo(Long hostId);
/**
* 使用用户配置获取 SSH 连接信息
*

View File

@ -5,6 +5,7 @@ import cd.casic.module.machine.dal.dataobject.MachineInfoDO;
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.enums.AuthenticationType;
import cd.casic.module.machine.utils.CryptogramUtil;
import cd.casic.module.terminal.common.ErrorMessage;
import cd.casic.module.terminal.controller.dto.TerminalConnectDTO;
@ -13,6 +14,7 @@ import cd.casic.module.terminal.host.config.model.HostSshConfigModel;
import cd.casic.module.terminal.host.extra.model.HostSshExtraModel;
import cd.casic.module.terminal.service.HostConnectService;
import cn.orionsec.kit.lang.utils.Valid;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@ -38,7 +40,17 @@ public class HostConnectServiceImpl implements HostConnectService {
private HostSshConfigModel CONFIG = HostSshConfigModel.builder().authType(HostSshAuthTypeEnum.KEY.name()).connectTimeout(10000).charset("UTF-8").fileContentCharset("UTF-8").fileNameCharset("UTF-8").build();
private final HostSshConfigModel CONFIG = HostSshConfigModel.builder().connectTimeout(10000).charset("UTF-8").fileContentCharset("UTF-8").fileNameCharset("UTF-8").build();
@Override
public TerminalConnectDTO getSshConnectInfo(Long hostId) {
log.info("HostConnectService.getSshConnectInfo-withHost hostId: {}", hostId);
// 查询主机
MachineInfoDO host = machineInfoMapper.selectById(hostId);
// 获取配置
return this.getHostConnectInfo(host, CONFIG, null);
}
@Override
@ -93,7 +105,7 @@ public class HostConnectServiceImpl implements HostConnectService {
conn.setHostName(host.getName());
// conn.setHostCode(host.getCode());
conn.setHostAddress(host.getHostIp());
conn.setHostPort(config.getPort());
conn.setHostPort(host.getSshPort());
conn.setTimeout(config.getConnectTimeout());
conn.setCharset(config.getCharset());
conn.setFileNameCharset(config.getFileNameCharset());
@ -116,9 +128,8 @@ public class HostConnectServiceImpl implements HostConnectService {
config.setAuthType(HostSshAuthTypeEnum.IDENTITY.name());
config.setIdentityId(extra.getIdentityId());
}
// 身份认证
HostSshAuthTypeEnum authType = HostSshAuthTypeEnum.of(config.getAuthType());
HostSshAuthTypeEnum authType = AuthenticationType.of(host.getAuthenticationType()).name().equals(AuthenticationType.SECRET_KEY.name()) ? HostSshAuthTypeEnum.KEY : HostSshAuthTypeEnum.PASSWORD;
if (HostSshAuthTypeEnum.IDENTITY.equals(authType)) {
// 身份认证
Valid.notNull(config.getIdentityId(), ErrorMessage.IDENTITY_ABSENT);
@ -138,13 +149,13 @@ public class HostConnectServiceImpl implements HostConnectService {
}
// 填充认证信息
conn.setUsername(config.getUsername());
conn.setUsername(host.getUsername());
if (HostSshAuthTypeEnum.PASSWORD.equals(authType)) {
// 密码认证
conn.setPassword(config.getPassword());
conn.setPassword(host.getPassword());
} else if (HostSshAuthTypeEnum.KEY.equals(authType)) {
// 密钥认证
Long keyId = config.getKeyId();
Long keyId = host.getSecretKeyId();
Valid.notNull(keyId, ErrorMessage.KEY_ABSENT);
SecretKeyDO key = secretKeyMapper.selectById(keyId);
Valid.notNull(key, ErrorMessage.KEY_ABSENT);

View File

@ -1,13 +1,27 @@
package cd.casic.module.terminal.service.impl;
import cd.casic.framework.commons.exception.ServiceException;
import cd.casic.framework.commons.pojo.CommonResult;
import cd.casic.framework.security.core.LoginUser;
import cd.casic.framework.security.core.util.SecurityFrameworkUtils;
import cd.casic.module.terminal.controller.dto.TerminalAccessDTO;
import cd.casic.module.terminal.controller.dto.TerminalConnectDTO;
import cd.casic.module.terminal.controller.dto.TerminalTransferDTO;
import cd.casic.module.terminal.dal.redis.TerminalRedisDAO;
import cd.casic.module.terminal.define.cache.TerminalCacheKeyDefine;
import cd.casic.module.terminal.enums.HostTypeEnum;
import cd.casic.module.terminal.enums.TerminalConnectTypeEnum;
import cd.casic.module.terminal.host.jsch.SessionMessage;
import cd.casic.module.terminal.host.jsch.SessionStores;
import cd.casic.module.terminal.service.HostConnectService;
import cn.orionsec.kit.lang.id.UUIds;
import static cd.casic.framework.commons.pojo.CommonResult.error;
import static cd.casic.framework.commons.pojo.CommonResult.success;
import cn.orionsec.kit.lang.utils.Strings;
import cn.orionsec.kit.lang.utils.Valid;
import cn.orionsec.kit.lang.utils.io.Streams;
import cn.orionsec.kit.net.host.SessionStore;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@ -25,6 +39,9 @@ public class TerminalService{
@Resource
private TerminalRedisDAO terminalRedisDAO;
@Resource
private HostConnectService hostConnectService;
public String getTerminalAccessToken() {
LoginUser user = Valid.notNull(SecurityFrameworkUtils.getLoginUser());
log.info("HostTerminalService.getTerminalAccessToken userId: {}", user.getId());
@ -54,4 +71,21 @@ public class TerminalService{
return transferToken;
}
public CommonResult testHostConnect(Long id) {
HostTypeEnum type = HostTypeEnum.of(TerminalConnectTypeEnum.SSH.name());
if (HostTypeEnum.SSH.equals(type)) {
// SSH 连接测试
SessionStore sessionStore = null;
TerminalConnectDTO info = null;
try {
info = hostConnectService.getSshConnectInfo(id);
sessionStore = SessionStores.openSessionStore(info);
} catch (Exception e) {
return error(new ServiceException().setMessage(Strings.format(SessionMessage.SERVER_UNREACHABLE,info.getHostAddress())));
} finally {
Streams.close(sessionStore);
}
}
return success();
}
}