2009-06-15 9 views
164

Кто-нибудь знает о хорошей библиотеке для входа в SSH с Java.SSH-библиотека для Java

+0

я имел обыкновение использовать Trilead SSH, но когда я проверил сайт сегодня кажется, что они отказываются от него. :(Это был мой абсолютный фаворит. –

+1

Кстати, похоже, что Trilead SSH2 активно поддерживается (в октябре 2013 года): [https://github.com/jenkinsci/trilead-ssh2] –

+2

Попробуйте [jcabi-ssh] (http://ssh.jcabi.com), это объясняется здесь http://www.yegor256.com/2014/09/02/java-ssh-client.html – yegor256

ответ

105

Java Secure Channel (JSCH) - очень популярная библиотека, используемая maven, ant и eclipse. Это с открытым исходным кодом с лицензией стиля BSD.

+1

Это выглядит хорошо - есть ли какие-либо javadocs для него на все?Примерами являются несколько плохо написанный код колебания (из краткой выборки, которую я сделал). –

+2

Вы хотите скачать источник из https://sourceforge.net/projects/jsch/files/jsch/jsch-0.1.42.zip/download и запустить «ant javadoc» –

+60

Я пытался использовать JSch некоторое время назад и не может понять, как это так популярно. Он не предлагает абсолютно никакой документации (даже в пределах источника) и ужасного дизайна API (http://techtavern.wordpress.com/2008/09/30/about-jsch-open-source-project/) – rluba

16

Взгляните на недавно выпущенный SSHD, который основан на проекте Apache MINA.

+1

Используя его, однако документации и примеров не хватает. –

55

Update: Проект GSOC и код там не активен, но это: https://github.com/hierynomus/sshj

hierynomus взял на себя в качестве сопровождающего с начала 2015 года Вот это старше, больше не поддерживается, Github ссылка:

https://github.com/shikhar/sshj


был проект GSOC:

http://code.google.com/p/commons-net-ssh/

Качество кода, кажется, лучше, чем JSch, что, хотя полная и рабочая реализация, не имеет документации. На странице Project отображается предстоящая бета-версия, последняя фиксация в репозитории была в середине августа.

Сравнить API:

http://code.google.com/p/commons-net-ssh/

SSHClient ssh = new SSHClient(); 
    //ssh.useCompression(); 
    ssh.loadKnownHosts(); 
    ssh.connect("localhost"); 
    try { 
     ssh.authPublickey(System.getProperty("user.name")); 
     new SCPDownloadClient(ssh).copy("ten", "/tmp"); 
    } finally { 
     ssh.disconnect(); 
    } 

http://www.jcraft.com/jsch/

Session session = null; 
Channel channel = null; 

try { 

JSch jsch = new JSch(); 
session = jsch.getSession(username, host, 22); 
java.util.Properties config = new java.util.Properties(); 
config.put("StrictHostKeyChecking", "no"); 
session.setConfig(config); 
session.setPassword(password); 
session.connect(); 

// exec 'scp -f rfile' remotely 
String command = "scp -f " + remoteFilename; 
channel = session.openChannel("exec"); 
((ChannelExec) channel).setCommand(command); 

// get I/O streams for remote scp 
OutputStream out = channel.getOutputStream(); 
InputStream in = channel.getInputStream(); 

channel.connect(); 

byte[] buf = new byte[1024]; 

// send '\0' 
buf[0] = 0; 
out.write(buf, 0, 1); 
out.flush(); 

while (true) { 
    int c = checkAck(in); 
    if (c != 'C') { 
     break; 
    } 

    // read '0644 ' 
    in.read(buf, 0, 5); 

    long filesize = 0L; 
    while (true) { 
     if (in.read(buf, 0, 1) < 0) { 
      // error 
      break; 
     } 
     if (buf[0] == ' ') { 
      break; 
     } 
     filesize = filesize * 10L + (long) (buf[0] - '0'); 
    } 

    String file = null; 
    for (int i = 0;; i++) { 
     in.read(buf, i, 1); 
     if (buf[i] == (byte) 0x0a) { 
      file = new String(buf, 0, i); 
      break; 
     } 
    } 

    // send '\0' 
    buf[0] = 0; 
    out.write(buf, 0, 1); 
    out.flush(); 

    // read a content of lfile 
    FileOutputStream fos = null; 

    fos = new FileOutputStream(localFilename); 
    int foo; 
    while (true) { 
     if (buf.length < filesize) { 
      foo = buf.length; 
     } else { 
      foo = (int) filesize; 
     } 
     foo = in.read(buf, 0, foo); 
     if (foo < 0) { 
      // error 
      break; 
     } 
     fos.write(buf, 0, foo); 
     filesize -= foo; 
     if (filesize == 0L) { 
      break; 
     } 
    } 
    fos.close(); 
    fos = null; 

    if (checkAck(in) != 0) { 
     System.exit(0); 
    } 

    // send '\0' 
    buf[0] = 0; 
    out.write(buf, 0, 1); 
    out.flush(); 

    channel.disconnect(); 
    session.disconnect(); 
} 

} catch (JSchException jsche) { 
    System.err.println(jsche.getLocalizedMessage()); 
} catch (IOException ioe) { 
    System.err.println(ioe.getLocalizedMessage()); 
} finally { 
    channel.disconnect(); 
    session.disconnect(); 
} 

} 
+2

Спасибо! Я использовал код Apache SSHD (который предлагает асинхронный API) как семя, которое дало проекту кикстарт. – shikhar

+1

Отлично. Я начал проект с использованием JSch, но мне очень нравится переключаться, если я получаю больше положительных отзывов о commons-net-ssh. – miku

+3

Я должен был упомянуть, что я был учеником GSOC :) – shikhar

20

Я только что обнаружил sshj, которая, кажется, гораздо более кратким API, чем JSch (но это требует Java 6). Документация в основном на примерах-в-репо на данный момент, и, как правило, этого достаточно для того, чтобы я смотрел в другом месте, но мне кажется достаточно хорошим, чтобы я мог сделать это в проекте, который только что начал.

+1

SSHJ на самом деле работает кем-то из внешнего мира. JSCH - это плохой документ и дизайн API со скрытыми и в значительной степени неразборчивыми зависимостями. Если вы не хотите тратить много времени на прохождение кода, чтобы попытаться выяснить, что происходит, используйте SSHJ. (И мне жаль, что я не был суров или актуален об АОХ. Я действительно это делаю.) –

+0

Да, sshj. Все, что я пробовал с ним работать: SCP, удаленное выполнение процессов, локальная и удаленная переадресация портов, агент-посредник с помощью [jsch-agent-proxy] (http://www.jcraft.com/jsch-agent-proxy). В АО был беспорядок. –

5

Существует новая версия Jsch up on github: https://github.com/vngx/vngx-jsch Некоторые из улучшений включают в себя: всесторонний javadoc, улучшенную производительность, улучшенную обработку исключений и лучшую привязку спецификации RFC. Если вы хотите каким-либо образом внести свой вклад, пожалуйста, откройте проблему или отправьте запрос на перенос.

+2

Слишком плохо, что не было новой фиксации более 3 лет. –

0

Я принял ответ miku и пример кода jsch. Затем мне пришлось скачать несколько файлов во время сеанса и сохранить оригинальные отметки времени. Это мой пример кода, как это сделать, вероятно, многие люди считают это полезным. Пожалуйста, игнорируйте filenameHack() функцию своей собственной usecase.

package examples; 

import com.jcraft.jsch.*; 
import java.io.*; 
import java.util.*; 

public class ScpFrom2 { 

    public static void main(String[] args) throws Exception { 
     Map<String,String> params = parseParams(args); 
     if (params.isEmpty()) { 
      System.err.println("usage: java ScpFrom2 " 
        + " user=myid password=mypwd" 
        + " host=myhost.com port=22" 
        + " encoding=<ISO-8859-1,UTF-8,...>" 
        + " \"remotefile1=/some/file.png\"" 
        + " \"localfile1=file.png\"" 
        + " \"remotefile2=/other/file.txt\"" 
        + " \"localfile2=file.txt\"" 

      ); 
      return; 
     } 

     // default values 
     if (params.get("port") == null) 
      params.put("port", "22"); 
     if (params.get("encoding") == null) 
      params.put("encoding", "ISO-8859-1"); //"UTF-8" 

     Session session = null; 
     try { 
      JSch jsch=new JSch(); 
      session=jsch.getSession(
        params.get("user"), // myuserid 
        params.get("host"), // my.server.com 
        Integer.parseInt(params.get("port")) // 22 
      ); 
      session.setPassword(params.get("password")); 
      session.setConfig("StrictHostKeyChecking", "no"); // do not prompt for server signature 

      session.connect(); 

      // this is exec command and string reply encoding 
      String encoding = params.get("encoding"); 

      int fileIdx=0; 
      while(true) { 
       fileIdx++; 

       String remoteFile = params.get("remotefile"+fileIdx); 
       String localFile = params.get("localfile"+fileIdx); 
       if (remoteFile == null || remoteFile.equals("") 
         || localFile == null || localFile.equals("")) 
        break; 

       remoteFile = filenameHack(remoteFile); 
       localFile = filenameHack(localFile); 

       try { 
        downloadFile(session, remoteFile, localFile, encoding); 
       } catch (Exception ex) { 
        ex.printStackTrace(); 
       } 
      } 

     } catch(Exception ex) { 
      ex.printStackTrace(); 
     } finally { 
      try{ session.disconnect(); } catch(Exception ex){} 
     } 
    } 

    private static void downloadFile(Session session, 
      String remoteFile, String localFile, String encoding) throws Exception { 
     // send exec command: scp -p -f "/some/file.png" 
     // -p = read file timestamps 
     // -f = From remote to local 
     String command = String.format("scp -p -f \"%s\"", remoteFile); 
     System.console().printf("send command: %s%n", command); 
     Channel channel=session.openChannel("exec"); 
     ((ChannelExec)channel).setCommand(command.getBytes(encoding)); 

     // get I/O streams for remote scp 
     byte[] buf=new byte[32*1024]; 
     OutputStream out=channel.getOutputStream(); 
     InputStream in=channel.getInputStream(); 

     channel.connect(); 

     buf[0]=0; out.write(buf, 0, 1); out.flush(); // send '\0' 

     // reply: T<mtime> 0 <atime> 0\n 
     // times are in seconds, since 1970-01-01 00:00:00 UTC 
     int c=checkAck(in); 
     if(c!='T') 
      throw new IOException("Invalid timestamp reply from server"); 

     long tsModified = -1; // millis 
     for(int idx=0; ; idx++){ 
      in.read(buf, idx, 1); 
      if(tsModified < 0 && buf[idx]==' ') { 
       tsModified = Long.parseLong(new String(buf, 0, idx))*1000; 
      } else if(buf[idx]=='\n') { 
       break; 
      } 
     } 

     buf[0]=0; out.write(buf, 0, 1); out.flush(); // send '\0' 

     // reply: C0644 <binary length> <filename>\n 
     // length is given as a text "621873" bytes 
     c=checkAck(in); 
     if(c!='C') 
      throw new IOException("Invalid filename reply from server"); 

     in.read(buf, 0, 5); // read '0644 ' bytes 

     long filesize=-1; 
     for(int idx=0; ; idx++){ 
      in.read(buf, idx, 1); 
      if(buf[idx]==' ') { 
       filesize = Long.parseLong(new String(buf, 0, idx)); 
       break; 
      } 
     } 

     // read remote filename 
     String origFilename=null; 
     for(int idx=0; ; idx++){ 
      in.read(buf, idx, 1); 
      if(buf[idx]=='\n') { 
       origFilename=new String(buf, 0, idx, encoding); // UTF-8, ISO-8859-1 
       break; 
      } 
     } 

     System.console().printf("size=%d, modified=%d, filename=%s%n" 
       , filesize, tsModified, origFilename); 

     buf[0]=0; out.write(buf, 0, 1); out.flush(); // send '\0' 

     // read binary data, write to local file 
     FileOutputStream fos = null; 
     try { 
      File file = new File(localFile); 
      fos = new FileOutputStream(file); 
      while(filesize > 0) { 
       int read = Math.min(buf.length, (int)filesize); 
       read=in.read(buf, 0, read); 
       if(read < 0) 
        throw new IOException("Reading data failed"); 

       fos.write(buf, 0, read); 
       filesize -= read; 
      } 
      fos.close(); // we must close file before updating timestamp 
      fos = null; 
      if (tsModified > 0) 
       file.setLastModified(tsModified);    
     } finally { 
      try{ if (fos!=null) fos.close(); } catch(Exception ex){} 
     } 

     if(checkAck(in) != 0) 
      return; 

     buf[0]=0; out.write(buf, 0, 1); out.flush(); // send '\0' 
     System.out.println("Binary data read");  
    } 

    private static int checkAck(InputStream in) throws IOException { 
     // b may be 0 for success 
     //   1 for error, 
     //   2 for fatal error, 
     //   -1 
     int b=in.read(); 
     if(b==0) return b; 
     else if(b==-1) return b; 
     if(b==1 || b==2) { 
      StringBuilder sb=new StringBuilder(); 
      int c; 
      do { 
       c=in.read(); 
       sb.append((char)c); 
      } while(c!='\n'); 
      throw new IOException(sb.toString()); 
     } 
     return b; 
    } 


    /** 
    * Parse key=value pairs to hashmap. 
    * @param args 
    * @return 
    */ 
    private static Map<String,String> parseParams(String[] args) throws Exception { 
     Map<String,String> params = new HashMap<String,String>(); 
     for(String keyval : args) { 
      int idx = keyval.indexOf('='); 
      params.put(
        keyval.substring(0, idx), 
        keyval.substring(idx+1) 
      ); 
     } 
     return params; 
    } 

    private static String filenameHack(String filename) { 
     // It's difficult reliably pass unicode input parameters 
     // from Java dos command line. 
     // This dirty hack is my very own test use case. 
     if (filename.contains("${filename1}")) 
      filename = filename.replace("${filename1}", "Korilla ABC ÅÄÖ.txt"); 
     else if (filename.contains("${filename2}")) 
      filename = filename.replace("${filename2}", "test2 ABC ÅÄÖ.txt");   
     return filename; 
    } 

} 
+0

Вы могли повторно использовать сеанс и избегать накладных расходов на подключение/отключение? –

0

http://code.google.com/p/connectbot/, Компиляция SRC \ COM \ trilead \ ssh2 на Windows Linux или Android, это может создать локальный порт форвардер или создать динамический порт Экспедитор или другой еще

Смежные вопросы