SSH不同版本使用jsch問題處理
- 1.問題一
- 2.問題二
- 2.1 說明
- 2.2 解決
- 3.問題三
1.問題一
# 1.系統
cat /etc/os-release
# 系統信息
NAME="openEuler"
VERSION="22.03 (LTS-SP1)"
ID="openEuler"
VERSION_ID="22.03"
PRETTY_NAME="openEuler 22.03 (LTS-SP1)"
ANSI_COLOR="0;31"# 2.ssh
ssh -V
# ssh信息
OpenSSH_8.8p1, OpenSSL 1.1.1m 14 Dec 2021
# 1.Java報錯
com.jcraft.jsch.JSchException: Algorithm negotiation fail# 2.使用 systemctl status sshd 查看狀態
Unable to negotiate with xxx.xxx.x.xxx port xxxxx: no matching host key type found.
Their offer: ssh-rsa,ssh-dss,ecdsa-sha2-nistp256,ecdsa-sha>
問題解決:
- 配置文件
sshd_config
的HostKeyAlgorithms
添加算法ssh-rsa
HostKeyAlgorithms ssh-ed25519,ssh-ed25519-cert-v01@openssh.com,rsa-sha2-256,rsa-sha2-512,ssh-rsa
2.問題二
2.1 說明
OpenSSH是從原本的OpenSSH_7.4p1, OpenSSL 1.0.2k-fips 26 Jan 2017
升級到 OpenSSH_9.7p1, OpenSSL 3.2.0 23 Nov 2023
,當前環境如下:
# 1.系統
CentOS Linux release 7.8.2003 (Core)# 2.ssh -V
OpenSSH_9.7p1, OpenSSL 3.2.0 23 Nov 2023
# 1.密碼錯誤時Java報錯信息
cn.hutool.extra.ssh.JschRuntimeException: JSchException: Auth fail
# 密碼修改正確后 systemctl status sshd 查看狀態
Accepted password for root from 123.160.246.239 port 22838 ssh2# 2.通過sftp上傳文件時
cn.hutool.extra.ssh.JschRuntimeException: JSchException: channel is not opened.
# 過一會兒后 systemctl status sshd 查看狀態
error: no more sessions
代碼進行調試,發現報錯的位置:
// 報錯行
channelSftp.put(fs, directory, ChannelSftp.OVERWRITE);
// 報錯信息
com.jcraft.jsch.SftpException: No such file
我一度認為是傳輸模式的問題,在ChannelSftp.put(InputStream src, String dst, int mode)
方法中,mode
參數用于指定文件傳輸模式,其可選值有:
- ChannelSftp.OVERWRITE:這是默認的傳輸模式,它會完全覆蓋目標服務器上的同名文件。如果目標文件不存在,則會創建一個新的文件。
- ChannelSftp.RESUME:這種模式下,如果傳輸被中斷,那么在下次調用
put
方法時,會從上一次中斷的地方繼續傳輸,而不是重新開始傳輸整個文件。 - ChannelSftp.APPEND:此模式允許你將數據追加到目標服務器上的現有文件中,而不是覆蓋它。如果目標文件不存在,則會創建一個新的文件。
換用其他模式依然報錯。
2.2 解決
最終發現是代碼的問題:
// 進行代碼調試時發現遠程登錄耗時較久
SshClient.getInstance().sshRemoteCallLoginByTool(sshHost, sshPort, sshUserName, sshPassword);
// 導致創建目標目錄時未執行
SshClient.getInstance().execCommandByTool("mkdir " + targetPath);
execCommandByTool方法的原始代碼:
/*** 執行命令** @param command 命令*/public String execCommandByTool(String command) {boolean isConnected = checkConnectionStatus();if (isConnected) {return JschUtil.exec(session, command, Charsets.UTF_8);}return null;}
可以看出,如果登錄較慢,checkConnectionStatus
的狀態是false
則創建目錄的命令未被執行,導致上傳時的文件路徑不存在,出現報錯信息,改進如下:
/*** 執行命令** @param command 命令*/
public String execCommandByTool(String command) {boolean isConnected = checkConnectionStatus();while (!isConnected) {isConnected = checkConnectionStatus();}return JschUtil.exec(session, command, Charsets.UTF_8);
}
3.問題三
# 連接超時異常
com.jcraft.jsch.JSchException: Session.connect: java.net.SocketTimeoutException: Read timed out
# 包損壞
com.jcraft.jsch.JSchException: Packet corrupt
異常出現的位置及處理方案:
private boolean createSessionAndConnected() {if (session == null) {log.info("SSHClient createSessionAndConnected...");session = JschUtil.createSession(this.sshHost, this.sshPort, this.sshUser, this.sshPass);Properties config = new Properties();config.put("StrictHostKeyChecking", "no");session.setConfig(config);try {session.connect(8000);// com.jcraft.jsch.JSchException: Session.connect: java.net.SocketTimeoutException: Read timed out// 增加超時時間 session.connect(200000);} catch (JSchException e) {// com.jcraft.jsch.JSchException: Packet corrupt// 使用同一個session對象再次進行連接時會報錯// 重新連接時使用新的session對象session = null;e.printStackTrace();log.info("SSHClient createSessionAndConnected printStackTrace");return false;}log.info("SSHClient createSessionAndConnected Success!");}return this.session.isConnected();
}