«1. Введение

SSH, также известный как Secure Shell или Secure Socket Shell, представляет собой сетевой протокол, который позволяет одному компьютеру безопасно подключаться к другому компьютеру через незащищенную сеть. В этом руководстве мы покажем, как установить соединение с удаленным SSH-сервером с помощью Java с помощью библиотек JSch и Apache MINA SSHD.

В наших примерах мы сначала откроем соединение SSH, затем выполним одну команду, прочитаем вывод и запишем его в консоль и, наконец, закроем соединение SSH. Мы постараемся максимально упростить пример кода.

2. JSch

JSch — это Java-реализация SSH2, которая позволяет нам подключаться к серверу SSH и использовать переадресацию портов, переадресацию X11 и передачу файлов. Кроме того, он распространяется под лицензией в стиле BSD и предоставляет нам простой способ установить SSH-соединение с Java.

Сначала добавим зависимость JSch Maven в наш файл pom.xml:

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

2.1. Реализация

Чтобы установить SSH-соединение с помощью JSch, нам нужны имя пользователя, пароль, 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();
        }
    }
}

Как видно из кода, сначала мы создаем сеанс клиента и настраиваем его для подключение к нашему SSH-серверу. Затем мы создаем клиентский канал, используемый для связи с сервером SSH, где мы указываем тип канала — в данном случае exec, что означает, что мы будем передавать команды оболочки на сервер.

Также мы должны установить выходной поток для нашего канала, куда будет записываться ответ сервера. После того, как мы установим соединение с помощью метода channel.connect(), команда передается, а полученный ответ записывается в консоль.

Давайте посмотрим, как использовать различные параметры конфигурации, которые предлагает JSch:

    StrictHostKeyChecking — указывает, будет ли приложение проверять, можно ли найти открытый ключ хоста среди известных хостов. Кроме того, доступны значения параметра ask, yes и no, где ask — значение по умолчанию. Если мы установим для этого свойства значение yes, JSch никогда автоматически не добавит ключ хоста в файл known_hosts и откажется подключаться к хостам, чей ключ хоста изменился. Это заставляет пользователя вручную добавлять все новые хосты. Если мы установим значение «нет», JSch автоматически добавит новый ключ хоста в список известных хостов. Доступные значения: zlib и none, где второе значение — сжатие по умолчанию. c2s — указывает, следует ли использовать сжатие для потока данных в направлении клиент-сервер. Доступные значения: zlib и none, где второе значение по умолчанию

Важно закрыть сеанс и канал SFTP после завершения связи с сервером, чтобы избежать утечек памяти.

3. Apache MINA SSHD

Apache MINA SSHD обеспечивает поддержку SSH для приложений на основе Java. Эта библиотека основана на Apache MINA, масштабируемой и высокопроизводительной асинхронной библиотеке ввода-вывода.

Давайте добавим зависимость Apache Mina SSHD Maven:

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

3.1. Реализация

Давайте посмотрим на пример кода подключения к серверу SSH с использованием Apache MINA SSHD:

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();
    }
}

При работе с Apache MINA SSHD у нас довольно похожая последовательность событий, что и с JSch. Сначала мы устанавливаем соединение с сервером SSH, используя экземпляр класса SshClient. Если мы инициализируем его с помощью SshClient.setupDefaultClient(), мы сможем работать с экземпляром, который имеет конфигурацию по умолчанию, подходящую для большинства случаев использования. Это включает в себя шифрование, сжатие, MAC, обмен ключами и подписи.

После этого мы создадим ClientChannel и присоединим к нему ByteArrayOutputStream, чтобы использовать его как поток ответа. Как мы видим, SSHD требует определенных тайм-аутов для каждой операции. Это также позволяет нам определить, как долго он будет ждать ответа сервера после передачи команды с помощью метода Channel.waitFor().

«Важно отметить, что SSHD запишет полный вывод консоли в поток ответов. JSch сделает это только с результатом выполнения команды.

Полная документация по Apache Mina SSHD доступна в официальном репозитории проекта на GitHub.

4. Заключение

В этой статье показано, как установить SSH-соединение с Java, используя две доступные библиотеки Java — JSch и Apache Mina SSHD. Мы также показали, как передать команду на удаленный сервер и получить результат выполнения. Кроме того, полные образцы кода доступны на GitHub.