目录
  1. 实现方式
  2. 代码
    1. 自己抽出来的工具类
    2. 工具类使用demo
  3. 参考
Java多层跳板机执行远程命令

可能是巧合吧,近期在开发过程中总是会遇到多层跳板机执行远程命令的场景。之前通过shell执行的情况,发现ssh命令存在一个参数-J可以支持多层跳板机,具体可以参考之前的一篇文章——ssh多层跳板机解决方法。但是当Java项目尝试去使用ssh执行远程命令时,发现无论是ganymed-ssh-2还是jsch都没有提供相应参数。

实现方式

经过一番搜索,恰巧在jsch官网找到JumpHost的示例,感觉实现方式大开眼界。原理如下图,假设需要通过A、B、C连接D服务器:

image-20190325165729882

代码

由于官网的demo代码比较啰嗦。于是简化了demo代码,写了一个自己的工具类。

自己抽出来的工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Optional;
import java.util.function.BiConsumer;

public class SshUtils {

/**
* 按顺序连接ssh服务器,并执行远程命令
* @param sshHosts ssh服务器列表,最后一个为最终的目标服务器
* @param command 需要执行的命令
* @param callback 输出回调,格式: (stdout, stderr) -> {}
*/
public static void execute(List<SshHost> sshHosts, String command, BiConsumer<InputStream, InputStream> callback) {
try {
JSch jsch = new JSch();
jsch.addIdentity("~/.ssh/id_rsa");
Session[] sessions = new Session[sshHosts.size()];
Session session = null;

for (int i = 0; i < sshHosts.size(); i++) {
SshHost sshHost = sshHosts.get(i);
if (i == 0) {
sessions[i] = session = jsch.getSession(sshHost.user, sshHost.host, sshHost.port);
} else {
int assignedPort = session.setPortForwardingL(0, sshHost.host, sshHost.port);
sessions[i] = session = jsch.getSession(sshHost.user, "127.0.0.1", assignedPort);
}
session.setConfig("StrictHostKeyChecking", "no");
session.connect();
}

ChannelExec channel = (ChannelExec)session.openChannel("exec");
channel.setCommand(command);
channel.connect();
callback.accept(channel.getInputStream(), channel.getErrStream());

for (int i = sessions.length - 1; i >= 0; i--) {
sessions[i].disconnect();
}
} catch (JSchException | IOException e) {
e.printStackTrace();
}
}

public static class SshHost {
String user;
String host;
Integer port;

public SshHost(String user, String host) {
this(user, host, 22);
}

public SshHost(String user, String host, Integer port) {
this.user = user;
this.host = host;
this.port = Optional.ofNullable(port).orElse(22);
}
}
}

工具类使用demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class SshUtilsTest {

@Test
public void execute() {
List<SshUtils.SshHost> sshHosts = new ArrayList<>();
sshHosts.add(new SshUtils.SshHost("root", "172.16.2.3", 22));
sshHosts.add(new SshUtils.SshHost("root", "10.3.2.123", 22));
sshHosts.add(new SshUtils.SshHost("root", "10.1.6.107", 22));
SshUtils.execute(sshHosts, "ls -l", (stdout, stderr) -> {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(stdout));
String line;
try {
while ((line = bufferedReader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
});
}
}

参考

[1] JSch - Examples - JumpHosts.java

文章作者: 谷河
文章链接: https://www.lyytaw.com/java/java%E5%A4%9A%E5%B1%82%E8%B7%B3%E6%9D%BF%E6%9C%BA%E6%89%A7%E8%A1%8C%E8%BF%9C%E7%A8%8Bssh/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 谷河|BLOG