Compare commits

..

2 Commits

Author SHA1 Message Date
唐潇凯
56150c156b Merge remote-tracking branch 'origin/jenkins-engin' into jenkins-engin 2025-06-05 11:01:56 +08:00
唐潇凯
891a332c59 机器连接修改 2025-06-05 11:00:54 +08:00
6 changed files with 54 additions and 40 deletions

View File

@ -69,8 +69,8 @@ public class MachineInfoController {
@PostMapping("/test") @PostMapping("/test")
@Operation(summary = "测试机器连接") @Operation(summary = "测试机器连接")
public CommonResult<Boolean> testConnection(@RequestBody MachineInfo machineInfo) { public CommonResult<Boolean> testConnection(@RequestParam Long id) {
return success(machineInfoService.testConnection(machineInfo)); return success(machineInfoService.testConnection(id));
} }
@GetMapping("/status/{machineName}") @GetMapping("/status/{machineName}")

View File

@ -30,7 +30,7 @@ public class MachineInfoDto extends PageDto {
private String status; private String status;
private String sshPort; private Integer sshPort;
private String password; private String password;

View File

@ -46,7 +46,7 @@ public class MachineInfo extends BaseEntity {
private String username; private String username;
//SSH端口号 //SSH端口号
@TableField(value = "SSH_port") @TableField(value = "ssh_port")
private Integer sshPort; private Integer sshPort;
@TableField(value = "password") @TableField(value = "password")

View File

@ -1,18 +1,15 @@
package cd.casic.module.machine.handler; package cd.casic.module.machine.handler;
import cd.casic.module.machine.utils.AliYunOssClient; import cd.casic.module.machine.utils.AliYunOssClient;
import cd.casic.module.machine.entity.MachineInfo; import cd.casic.module.machine.entity.MachineInfo;
import cd.casic.module.machine.entity.SecretKey; import cd.casic.module.machine.entity.SecretKey;
import cd.casic.module.machine.enums.AuthenticationType;
import cd.casic.module.machine.enums.ConnectionStatus; import cd.casic.module.machine.enums.ConnectionStatus;
import cd.casic.module.machine.service.SecretKeyService; import cd.casic.module.machine.service.SecretKeyService;
import com.jcraft.jsch.*; import com.jcraft.jsch.*;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StreamUtils; import org.springframework.util.StreamUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import java.io.*; import java.io.*;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Objects; import java.util.Objects;
@ -23,6 +20,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
* 优化后的SSH连接会话类 * 优化后的SSH连接会话类
*/ */
@Slf4j @Slf4j
@Component
public class ConnectionSession implements AutoCloseable { public class ConnectionSession implements AutoCloseable {
@Resource @Resource
SecretKeyService secretKeyService; SecretKeyService secretKeyService;
@ -30,21 +28,29 @@ public class ConnectionSession implements AutoCloseable {
@Resource @Resource
AliYunOssClient aliYunOssClient; AliYunOssClient aliYunOssClient;
private final MachineInfo machineInfo; private MachineInfo machineInfo;
private Session sshSession; private Session sshSession;
private ConnectionStatus status = ConnectionStatus.DISCONNECTED; private ConnectionStatus status = ConnectionStatus.DISCONNECTED;
private final AtomicBoolean isExecuting = new AtomicBoolean(false); private final AtomicBoolean isExecuting = new AtomicBoolean(false);
// 连接配置常量 // todo连接配置常量
private static final int CONNECTION_TIMEOUT = 5000; // 连接超时时间(毫秒) private static final int CONNECTION_TIMEOUT = 5000; // 连接超时时间(毫秒)
private static final int COMMAND_TIMEOUT = 30000; // 命令执行超时时间(毫秒) private static final int COMMAND_TIMEOUT = 30000; // 命令执行超时时间(毫秒)
private static final int RETRY_COUNT = 3; // 重试次数 private static final int RETRY_COUNT = 3; // 重试次数
private static final int RETRY_DELAY = 1000; // 重试间隔(毫秒) private static final int RETRY_DELAY = 1000; // 重试间隔(毫秒)
public ConnectionSession(MachineInfo machineInfo) { public ConnectionSession() {
this.machineInfo = Objects.requireNonNull(machineInfo, "MachineInfo cannot be null");
} }
// 使用setter注入MachineInfo
public void setMachineInfo(MachineInfo machineInfo) {
this.machineInfo = Objects.requireNonNull(machineInfo, "MachineInfo cannot be null");
log.debug("MachineInfo 已注入: {}", machineInfo.getHostIp());
}
/** /**
* 建立SSH连接支持重试机制 * 建立SSH连接支持重试机制
*/ */
@ -121,25 +127,32 @@ public class ConnectionSession implements AutoCloseable {
* 配置认证方式密码或密钥 * 配置认证方式密码或密钥
*/ */
private void configureAuthentication(JSch jsch) throws JSchException { private void configureAuthentication(JSch jsch) throws JSchException {
if (machineInfo.getAuthenticationType() == AuthenticationType.SECRET_KEY) { if (machineInfo.getAuthenticationTypeCode() == 2) {
// 密钥认证 // 密钥认证
if (machineInfo.getSecretKeyId() == null) { if (machineInfo.getSecretKeyId() == null) {
throw new JSchException("Secret key ID is required for key-based authentication"); throw new JSchException("Secret key ID is required for key-based authentication");
} }
String privateKeyContent = getPrivateKeyContent(machineInfo.getSecretKeyId()); String privateKeyContent = getPrivateKeyContent(machineInfo.getSecretKeyId());
if (StringUtils.isEmpty(privateKeyContent)) { // 验证私钥格式
throw new JSchException("Private key content is empty or null for ID: " + machineInfo.getSecretKeyId()); if (!privateKeyContent.startsWith("-----BEGIN")) {
throw new JSchException("Invalid private key format. Expected OpenSSH format.");
} }
// 加载私钥支持密码短语可从配置中获取 try {
// 尝试加载私钥
jsch.addIdentity( jsch.addIdentity(
machineInfo.getName(), machineInfo.getName(),
privateKeyContent.getBytes(StandardCharsets.UTF_8), privateKeyContent.getBytes(StandardCharsets.UTF_8),
null, null,
null // 密码短语可为null null
); );
} else if (machineInfo.getAuthenticationType() == AuthenticationType.PASSWORD) { log.info("Private key loaded successfully for {}", machineInfo.getHostIp());
} catch (JSchException e) {
log.error("Failed to load private key: {}", e.getMessage());
throw e;
}
} else if (machineInfo.getAuthenticationTypeCode() == 1) {
// 密码认证 // 密码认证
if (StringUtils.isEmpty(machineInfo.getPassword())) { if (StringUtils.isEmpty(machineInfo.getPassword())) {
throw new JSchException("Password is required for password-based authentication"); throw new JSchException("Password is required for password-based authentication");

View File

@ -28,10 +28,10 @@ public interface MachineInfoService extends IService<MachineInfo> {
/** /**
* 测试机器连接 * 测试机器连接
* *
* @param machineInfo 机器信息 * @param id 机器id
* @return 连接是否成功 * @return 连接是否成功
*/ */
boolean testConnection(MachineInfo machineInfo); boolean testConnection(Long id);
/** /**
* 获取机器连接状态 * 获取机器连接状态

View File

@ -42,7 +42,8 @@ public class MachineinfoServiceImpl extends ServiceImpl<MachineInfoMapper, Machi
@Resource @Resource
private MachineInfoMapper machineInfoMapper; private MachineInfoMapper machineInfoMapper;
@Resource
private ConnectionSession connectionSession;
/** /**
* 会话ID生成器 * 会话ID生成器
*/ */
@ -184,14 +185,19 @@ public class MachineinfoServiceImpl extends ServiceImpl<MachineInfoMapper, Machi
@Override @Override
public boolean testConnection(MachineInfo machineInfo) { public boolean testConnection(Long id) {
if (machineInfo.getStatus().getCode() == UN_ENABLE) { //先查询机器是否存在在判断机器可用性
MachineInfo machineInfo = this.getById(id);
if (machineInfo==null){
throw new RuntimeException("机器不存在");
}
if (machineInfo.getStatusCode() == 0) {
throw new RuntimeException("机器不可用"); throw new RuntimeException("机器不可用");
} }
log.info("测试机器连接: {}", machineInfo.getHostIp()); log.info("测试机器连接: {}", machineInfo.getHostIp());
connectionSession.setMachineInfo(machineInfo);
try (ConnectionSession session = createSession(machineInfo)) { try{
session.connect(); connectionSession.connect();
return true; return true;
} catch (Exception e) { } catch (Exception e) {
log.error("机器连接测试失败: {}", e.getMessage(), e); log.error("机器连接测试失败: {}", e.getMessage(), e);
@ -240,14 +246,15 @@ public class MachineinfoServiceImpl extends ServiceImpl<MachineInfoMapper, Machi
} }
try { try {
ConnectionSession session = createSession(machineInfo); connectionSession.setMachineInfo(machineInfo);
session.connect();
connectionSession.connect();
// 生成会话ID // 生成会话ID
String sessionId = generateSessionId(); String sessionId = generateSessionId();
// 保存会话 // 保存会话
sessions.put(sessionId, session); sessions.put(sessionId, connectionSession);
machineSessionMapping.put(machineInfo.getName(), sessionId); machineSessionMapping.put(machineInfo.getName(), sessionId);
log.info("机器连接成功: {}, 会话ID: {}", machineInfo.getHostIp(), sessionId); log.info("机器连接成功: {}, 会话ID: {}", machineInfo.getHostIp(), sessionId);
@ -346,12 +353,6 @@ public class MachineinfoServiceImpl extends ServiceImpl<MachineInfoMapper, Machi
} }
} }
/**
* 创建连接会话
*/
private ConnectionSession createSession(MachineInfo machineInfo) {
return new ConnectionSession(machineInfo);
}
/** /**
* 生成会话ID * 生成会话ID