SSHJ 简介

SSHJ是一个开源 Java 库,它使用SSH协议与远程服务器进行安全通信。

在本文中,我们将介绍 SSHJ 库的基本功能。

依赖关系
要使用 SSHJ 库,我们必须将以下依赖项添加到项目中:

<dependency>
    <groupId>com.hierynomus</groupId>
    <artifactId>sshj</artifactId>
    <version>0.38.0</version>
</dependency>

我们可以在 Maven Central 中找到最新版本的SSHJ库。

SSHJ库
SSHJ 库帮助通过 SSH 建立与远程服务器的安全连接。

借助SSHJ 库,我们可以使用 SCP 或 SFTP 协议处理文件上传和下载。另外,我们还有一个额外的好处,那就是能够使用它进行本地端口转发和远程端口转发。

连接SSH客户端
SSH 客户端可以使用密码或公钥身份验证连接到服务器。 SSHJ 库使我们能够使用任一方法登录。

1.密码认证
我们可以使用 SSHJ 库通过 SSH 端口连接到服务器。需要为 SSH 连接指定主机名、端口、用户名和密码。

SSH 客户端使用authPassword()方法通过密码身份验证连接到服务器:

String host = // ...
int port =
// ...
String username =
// ...
String password =
// ...
SSHClient client = new SSHClient();
client.addHostKeyVerifier(new PromiscuousVerifier());
client.connect(host, port);
client.authPassword(username, password);

正如我们在上面的代码中看到的,我们使用密码身份验证将客户端连接到主机。

2.公钥认证
我们也可以使用公钥连接到服务器。为了使用公钥进行连接,我们需要在服务器上的known_hosts文件中有一个文件条目,或者我们可以在客户端计算机上为远程服务器生成一个公钥,并将公钥复制到服务器上的授权SSH密钥中。

SSH 客户端使用authPublickey()方法通过公钥身份验证连接到服务器:

String host = // ... 
String username =
// ... 
File privateKeyFile =
// ... 
int port =
// ...
SSHClient client = new SSHClient();
KeyProvider privateKey = client.loadKeys(privateKeyFile.getPath());
client.addHostKeyVerifier(new PromiscuousVerifier());
client.connect(host, port);
client.authPublickey(username, privateKey);

我们可以为客户端生成一个公钥,并在要连接的服务器上更新它。在其余示例中,我们将使用第一种方法登录,即使用用户名和密码。

通过 SSH 执行命令
我们可以在连接到服务器的sshClient 启动的会话 上使用exec()方法通过 SSHJ 库执行命令:

SSHClient client = new SSHClient();
Session session = sshClient.startSession();
Command cmd = session.exec("ls -lsa");
BufferedReader reader = new BufferedReader(new InputStreamReader(cmd.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
    System.out.println(line);
}
cmd.join(5, TimeUnit.SECONDS);
session.close();

正如我们在上面的代码中看到的,我们为sshClient启动一个会话。然后,我们执行ls -lsa命令,该命令列出了目录中的所有文件。然后,我们使用BufferedReader来读取所执行命令的输出。

同样,这里也可以执行其他命令。

通过SCP上传/下载文件
我们可以通过SCP上传文件。对于上传,我们使用SCPFileTransfer对象的upload()方法:

String filePath = // ... 
SSHClient ssh = new SSHClient();
ssh.useCompression();
ssh.newSCPFileTransfer()
  .upload(new FileSystemFile(filePath),
"/upload/");

在这里,我们将文件传输到服务器上的上传目录。

useCompression()方法将zlib压缩添加到首选算法中,这可以显着提高速度。无法保证谈判会成功。如果客户端已经连接,则重新协商;否则,它只会返回。我们也可以在连接客户端之前使用useCompression() 。

对于 SCP 文件下载,我们在SCPFileTransfer对象上使用download() 方法:

String downloadPath = // ...
String fileName =
// ...
SSHClient ssh = new SSHClient();
ssh.useCompression();
ssh.newSCPFileTransfer()
  .download(
"/upload/" + fileName, downloadPath);

在这里,我们将文件从服务器上的upload目录下载到客户端上的downloadPath位置。

上述上传和下载方法在内部运行scp命令,使用 SSH 连接将文件从本地计算机复制到远程服务器,反之亦然。

通过SFTP上传/下载文件
我们可以通过SFTP上传文件。对于上传,我们使用SFTPClient对象上的put()方法:

String filePath = // ...
SSHClient ssh = new SSHClient();
SFTPClient sftp = ssh.newSFTPClient();
sftp.put(new FileSystemFile(filePath),
"/upload/");

这里,我们将文件从客户端上的用户主目录传输到服务器上的上传目录。

对于 SFTP 下载,我们使用SFTPClient对象上的get()  方法:

String downloadPath = // ...
String fileName =
// ...
SSHClient ssh = new SSHClient();
SFTPClient sftp = ssh.newSFTPClient();
sftp.get(
"/upload/" + fileName, downloadPath);
sftp.close();

在这里,我们将文件从服务器上的upload目录下载到客户端上的downloadPath位置。

本地端口转发
本地端口转发用于访问远程服务器上的服务,就像服务在客户端上运行一样:

SSHClient ssh = new SSHClient();
Parameters params = new Parameters(ssh.getRemoteHostname(), 8081, "google.com", 80);
ServerSocket ss = new ServerSocket();
ss.setReuseAddress(true);
ss.bind(new InetSocketAddress(params.getLocalHost(), params.getLocalPort()));
ssh.newLocalPortForwarder(params, ss)
  .listen();

在这里,我们将服务器的80端口转发到客户端计算机的8081端口,以便我们可以从客户端计算机上的8081端口访问服务器80端口上托管的网站或服务。

远程端口转发
使用远程端口转发,我们可以将客户端计算机上运行的服务公开到远程服务器网络:

SSHClient ssh = new SSHClient();
ssh.getConnection()
  .getKeepAlive()
  .setKeepAliveInterval(5);
ssh.getRemotePortForwarder()
  .bind(new Forward(8083), new SocketForwardingConnectListener(new InetSocketAddress("google.com", 80)));
ssh.getTransport()
  .join();

这里,我们将运行在客户端8083端口的服务转发到远程服务器的80端口。实际上,在客户端计算机上的8083端口上运行的服务暴露在远程服务器的80端口上。

对于本地和远程端口转发,我们需要确保正确的防火墙设置到位。

检查连接丢失
我们需要检查连接丢失以监控服务器连接状态和健康状况。 SSHJ 提供了使用 keepalive 检查连接丢失的选项:

String hostName = // ...
String userName =
// ...
String password =
// ...
DefaultConfig defaultConfig = new DefaultConfig();
defaultConfig.setKeepAliveProvider(KeepAliveProvider.KEEP_ALIVE);
SSHClient ssh = new SSHClient(defaultConfig);
ssh.addHostKeyVerifier(new PromiscuousVerifier());
ssh.connect(hostName, 22);
ssh.getConnection()
  .getKeepAlive()
  .setKeepAliveInterval(5);
ssh.authPassword(userName, password);
Session session = ssh.startSession();
session.allocateDefaultPTY();
new CountDownLatch(1).await();
session.allocateDefaultPTY();
session.close();
ssh.disconnect();

在上面的代码中,我们可以看到配置KeepAliveProvider.KEEP_ALIVE启用了SSHJ库的保活模式。

我们使用setKeepAliveInterval()来设置来自客户端的两个保持活动消息之间的间隔。