Published on

使用Java连接SSH

Authors
  • avatar
    Name
    ReLive27
    Twitter

简介

SSH 为建立在应用层基础上的安全协议。SSH 是较可靠,专为远程登录会话和其他网络服务提供安全性的协议。

Jsch

JSch 是 SSH2的纯 Java 实现。JSch 允许你连接到一个 sshd 服务器并使用端口转发、X11 转发、文件传输等,你可以将它的功能集成到你自己的 Java 程序中。

首先,让我们将JSch Maven 依赖添加到我们的pom.xml文件中:

<dependency>
    <groupId>com.jcraft</groupId>
    <artifactId>jsch</artifactId>
    <version>0.1.55</version>
</dependency>

执行

要使用Jsch连接ssh,需要提供用户名密码主机端口,以下是简单代码实例:

 public static void execCommand(String username, String password, String host, Integer port, String command) throws JSchException, InterruptedException {
        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);
            ByteArrayOutputStream errorResponseStream = new ByteArrayOutputStream();
            channel.setErrStream(errorResponseStream);
            channel.connect();

            while (channel.isConnected()) {
                Thread.sleep(100);
            }

            String responseString = new String(responseStream.toByteArray(), StandardCharsets.UTF_8);
            if (!responseString.isEmpty()) {
                log.info(responseString);
            }
            String errorResponseString = new String(errorResponseStream.toByteArray(), StandardCharsets.UTF_8);
            if (!errorResponseString.isEmpty()) {
                log.warn(errorResponseString);
            }
        } finally {
            if (session != null) {
                session.disconnect();
            }
            if (channel != null) {
                channel.disconnect();
            }
        }
    }

上述代码中我们首先建立SSH会话,通过会话建立执行通道,执行我们提供的shell命令,并将响应信息和错误响应信息输出到控制台。

如何使用 JSch 提供的不同配置参数

  • StrictHostKeyChecking - 它指示应用程序是否将检查是否可以在已知主机中找到主机公钥。此外,可用的参数值是askyesno,其中ask是默认值。如果我们将此属性设置为yes,JSch 将永远不会自动将主机密钥添加到known_hosts文件,并且它将拒绝连接到主机密钥已更改的主机。这会强制用户手动添加所有新主机。如果我们将其设置为 no,JSch 会自动将新的主机密钥添加到已知主机列表中
  • compression.s2c – 指定是否对从服务器到客户端应用程序的数据流使用压缩。可用值为zlibnone,其中第二个是默认值
  • compression.c2s – 指定是否对客户端-服务器方向的数据流使用压缩。可用值为zlibnone,其中第二个是默认值

Apache MINA SSHD

Apache SSHD 是一个 100% 纯 Java 库,支持客户端和服务器端的 SSH 协议。该库基于Apache MINA,这是一个可扩展的高性能异步 IO 库。

首先,让我们将sshd Maven 依赖添加到我们的pom.xml文件中:

<dependency>
    <groupId>org.apache.sshd</groupId>
    <artifactId>sshd-core</artifactId>
    <version>2.8.0</version>
</dependency>

执行

同样,建立ssh连接需要提供用户名密码主机地址端口,除此之外,sshd可以设置连接超时时间,以下提供简单的代码示例:

 public static void execCommand(String username, String password, String host, Integer port, Integer 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();
                 ByteArrayOutputStream errorResponseStream = new ByteArrayOutputStream();
                 ClientChannel channel = session.createChannel(Channel.CHANNEL_SHELL)) {
                channel.setOut(responseStream);
                channel.setErr(errorResponseStream);
                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 errorString = new String(errorResponseStream.toByteArray());
                    if (!errorString.isEmpty()) {
                        log.warn(errorString);
                    }
                    String responseString = new String(responseStream.toByteArray());
                    if (!responseString.isEmpty()) {
                        log.info(responseString);
                    }
                } finally {
                    channel.close(false);
                }
            }
        } finally {
            client.stop();
        }
    }

结论

与往常一样,本文中使用的源代码可在 GitHub 上获得。