1. Introduction
1.绪论
SSH, also known as Secure Shell or Secure Socket Shell, is a network protocol that allows one computer to securely connect to another computer over an unsecured network. In this tutorial, we’ll show how to establish a connection to a remote SSH server with Java using the JSch and Apache MINA SSHD libraries.
SSH,也被称为安全外壳或安全套接字,是一种网络协议,允许一台计算机通过不安全的网络安全地连接到另一台计算机。在本教程中,我们将展示如何使用JSch和Apache MINA SSHD库,用Java建立与远程SSH服务器的连接。
In our examples, we’ll first open the SSH connection, then execute one command, read the output and write it to the console, and, finally, close the SSH connection. We’ll keep the sample code as simple as possible.
在我们的例子中,我们将首先打开SSH连接,然后执行一条命令,读取输出并将其写入控制台,最后,关闭SSH连接。我们将尽可能地保持示例代码的简单。
2. JSch
2.ǞǞǞ
JSch is the Java implementation of SSH2 that allows us to connect to an SSH server and use port forwarding, X11 forwarding, and file transfer. Also, it is licensed under the BSD style license and provides us with an easy way to establish an SSH connection with Java.
JSch是SSH2的Java实现,允许我们连接到SSH服务器并使用端口转发、X11转发和文件传输。此外,它还获得了BSD风格的许可,并为我们提供了一种用Java建立SSH连接的简单方法。
First, let’s add the JSch Maven dependency to our pom.xml file:
首先,让我们把JSch Maven依赖性添加到我们的pom.xml文件。
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.55</version>
</dependency>
2.1. Implementation
2.1.实施
To establish an SSH connection using JSch, we need a username, password, host URL, and SSH port. The default SSH port is 22, but it could happen that we’ll configure the server to use other port for SSH connections:
要使用JSch建立SSH连接,我们需要一个用户名、密码、主机URL和SSH端口。默认的SSH端口是22,但有可能我们会将服务器配置为使用其他端口进行SSH连接。
public static void listFolderStructure(String username, String password,
String host, int port, String command) throws Exception {
Session session = null;
ChannelExec channel = null;
try {
session = new JSch().getSession(username, host, port);
session.setPassword(password);
session.setConfig("StrictHostKeyChecking", "no");
session.connect();
channel = (ChannelExec) session.openChannel("exec");
channel.setCommand(command);
ByteArrayOutputStream responseStream = new ByteArrayOutputStream();
channel.setOutputStream(responseStream);
channel.connect();
while (channel.isConnected()) {
Thread.sleep(100);
}
String responseString = new String(responseStream.toByteArray());
System.out.println(responseString);
} finally {
if (session != null) {
session.disconnect();
}
if (channel != null) {
channel.disconnect();
}
}
}
As we can see in the code, we first create a client session and configure it for connection to our SSH server. Then, we create a client channel used to communicate with the SSH server where we provide a channel type – in this case, exec, which means that we’ll be passing shell commands to the server.
正如我们在代码中看到的,我们首先创建一个客户端会话,并配置它与我们的SSH服务器连接。然后,我们创建一个客户端通道,用于与SSH服务器通信,我们提供一个通道类型–在这种情况下,exec,这意味着我们将向服务器传递shell命令。
Also, we should set the output stream for our channel where the server response will be written. After we establish the connection using the channel.connect() method, the command is passed, and the received response is written on the console.
另外,我们应该为我们的通道设置输出流,服务器的响应将被写入其中。在我们使用channel.connect()方法建立连接后,命令被传递,收到的响应被写在控制台。
Let’s see how to use different configuration parameters that JSch offers:
让我们看看如何使用JSch提供的不同配置参数。
- StrictHostKeyChecking – it indicates whether the application will check if the host public key could be found among known hosts. Also, available parameter values are ask, yes, and no, where ask is the default. If we set this property to yes, JSch will never automatically add the host key to the known_hosts file, and it’ll refuse to connect to hosts whose host key has changed. This forces the user to manually add all new hosts. If we set it to no, JSch will automatically add a new host key to the list of known hosts
- compression.s2c – specifies whether to use compression for the data stream from the server to our client application. Available values are zlib and none where the second is the default
- compression.c2s – specifies whether to use compression for the data stream in the client-server direction. Available values are zlib and none where the second is the default
It’s important to close the session and the SFTP channel after the communication with the server is over to avoid memory leaks.
重要的是,在与服务器的通信结束后要关闭会话和SFTP通道,以避免内存泄漏。
3. Apache MINA SSHD
3.阿帕奇MINA SSHD
Apache MINA SSHD provides SSH support for Java-based applications. This library is based on Apache MINA, a scalable and high-performance asynchronous IO library.
Apache MINA SSHD为基于 Java 的应用程序提供 SSH 支持。该库基于Apache MINA,这是一个可扩展和高性能的异步IO库。
Let’s add the Apache Mina SSHD Maven dependency:
我们来添加Apache Mina SSHD Maven依赖项。
<dependency>
<groupId>org.apache.sshd</groupId>
<artifactId>sshd-core</artifactId>
<version>2.5.1</version>
</dependency>
3.1. Implementation
3.1 实施
Let’s see the code sample of connecting to the SSH server using Apache MINA SSHD:
让我们看看使用Apache MINA SSHD连接到SSH服务器的代码示例。
public static void listFolderStructure(String username, String password,
String host, int port, long defaultTimeoutSeconds, String command) throws IOException {
SshClient client = SshClient.setUpDefaultClient();
client.start();
try (ClientSession session = client.connect(username, host, port)
.verify(defaultTimeoutSeconds, TimeUnit.SECONDS).getSession()) {
session.addPasswordIdentity(password);
session.auth().verify(defaultTimeoutSeconds, TimeUnit.SECONDS);
try (ByteArrayOutputStream responseStream = new ByteArrayOutputStream();
ClientChannel channel = session.createChannel(Channel.CHANNEL_SHELL)) {
channel.setOut(responseStream);
try {
channel.open().verify(defaultTimeoutSeconds, TimeUnit.SECONDS);
try (OutputStream pipedIn = channel.getInvertedIn()) {
pipedIn.write(command.getBytes());
pipedIn.flush();
}
channel.waitFor(EnumSet.of(ClientChannelEvent.CLOSED),
TimeUnit.SECONDS.toMillis(defaultTimeoutSeconds));
String responseString = new String(responseStream.toByteArray());
System.out.println(responseString);
} finally {
channel.close(false);
}
}
} finally {
client.stop();
}
}
When working with the Apache MINA SSHD, we have a pretty similar sequence of events as with JSch. First, we establish a connection to an SSH server using the SshClient class instance. If we initialize it with SshClient.setupDefaultClient(), we’ll be able to work with the instance that has a default configuration suitable for most use cases. This includes ciphers, compression, MACs, key exchanges, and signatures.
当使用Apache MINA SSHD时,我们有一个与JSch相当类似的事件顺序。首先,我们使用SshClient类实例建立一个与SSH服务器的连接。如果我们用SshClient.setupDefaultClient()来初始化它,我们就可以使用这个实例,它的默认配置适合大多数的使用情况。这包括密码器、压缩、MAC、密钥交换和签名。
After that, we’ll create ClientChannel and attach the ByteArrayOutputStream to it, so that we’ll use it as a response stream. As we can see, SSHD requires defined timeouts for every operation. It also allows us to define how long it will wait for server response after the command is passed by using Channel.waitFor() method.
之后,我们将创建ClientChannel并将ByteArrayOutputStream附加到它,这样我们将把它作为一个响应流。正如我们所看到的,SSHD需要为每个操作定义超时。它还允许我们通过使用Channel.waitFor()方法来定义在命令传递后等待服务器响应的时间。
It’s important to notice that SSHD will write complete console output into the response stream. JSch will do it only with the command execution result.
重要的是要注意SSHD将把完整的控制台输出写入响应流中。JSch将只写命令的执行结果。
Complete documentation on Apache Mina SSHD is available on the project’s official GitHub repository.
关于Apache Mina SSHD的完整文档可在项目的官方GitHub存储库中找到。
4. Conclusion
4.总结
This article illustrated how to establish an SSH connection with Java using two of the available Java libraries – JSch and Apache Mina SSHD. We also showed how to pass the command to the remote server and get the execution result. Also, complete code samples are available over on GitHub.
本文说明了如何使用两个可用的Java库–JSch和Apache Mina SSHD,用Java建立一个SSH连接。我们还展示了如何将命令传递给远程服务器并获得执行结果。此外,完整的代码样本可在GitHub上获得。