2009-06-15 40 views
164

Có ai biết thư viện tốt để đăng nhập SSH từ Java không.Thư viện SSH cho Java

+0

tôi sử dụng để sử dụng Trilead SSH nhưng khi tôi kiểm tra trang web ngày nay có vẻ như họ đang từ bỏ nó. :(Đó là số yêu thích tuyệt đối của tôi –

+1

BTW, có vẻ như Trilead SSH2 đang được duy trì tích cực (vào tháng 10 năm 2013): [https://github.com/jenkinsci/trilead-ssh2] –

+2

Thử [jcabi-ssh] (http://ssh.jcabi.com), nó được giải thích ở đây http://www.yegor256.com/2014/09/02/java-ssh-client.html – yegor256

Trả lời

105

Java Secure Channel (JSCH) là một thư viện rất phổ biến, được sử dụng bởi maven, ant và eclipse. Nó là mã nguồn mở với một giấy phép kiểu BSD.

+1

Điều đó có vẻ tốt - có bất kỳ javadocs nào cho nó tại tất cả các?Các ví dụ có phần viết kém mã swing (từ lấy mẫu ngắn gọn tôi đã làm). –

+2

Bạn đã tải xuống nguồn từ https://sourceforge.net/projects/jsch/files/jsch/jsch-0.1.42.zip/download và chạy "ant javadoc" –

+60

Tôi đã thử sử dụng JSch một thời gian trước và không thể hiểu nó nổi tiếng thế nào. Nó cung cấp hoàn toàn không có tài liệu (không phải ngay cả trong nguồn) và một thiết kế API khủng khiếp (http://techtavern.wordpress.com/2008/09/30/about-jsch-open-source-project/ tổng hợp nó lên khá tốt) – rluba

16

Hãy xem bản phát hành gần đây SSHD, dựa trên dự án Apache MINA.

+1

Sử dụng nó, tuy nhiên vẫn thiếu tài liệu và ví dụ. –

55

Cập nhật: Dự án GSOC và mã không có hoạt động, nhưng đây là: https://github.com/hierynomus/sshj

hierynomus nhậm chức duy trì kể từ đầu năm 2015. Đây là không còn được duy trì, Github liên kết cũ,:

https://github.com/shikhar/sshj


có một dự án GSOC:

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

Chất lượng mã dường như tốt hơn JSch, trong đó, trong khi triển khai hoàn chỉnh và hoạt động, thiếu tài liệu. Trang dự án phát hiện một bản phát hành beta sắp tới, lần commit cuối cùng tới kho lưu trữ là giữa tháng tám.

Hãy so sánh các 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

Cảm ơn! Tôi đã sử dụng mã Apache SSHD (cung cấp một API không đồng bộ) làm hạt giống cho dự án một khởi đầu. – shikhar

+1

Tuyệt vời. Tôi bắt đầu một dự án sử dụng JSch, nhưng tôi thực sự muốn chuyển đổi, nếu tôi nghe phản hồi tích cực hơn về commons-net-ssh. – miku

+3

Tôi nên nói rằng tôi là sinh viên GSOC :) – shikhar

20

Tôi chỉ phát hiện sshj, mà dường như có một API nhiều súc tích hơn JSCH (nhưng nó đòi hỏi Java 6). Các tài liệu chủ yếu là bởi các ví dụ-in-the-repo vào thời điểm này, và thường là đủ cho tôi để tìm nơi khác, nhưng nó có vẻ tốt, đủ cho tôi để cho nó một shot trên một dự án tôi chỉ mới bắt đầu.

+1

SSHJ thực sự khả thi bởi một ai đó từ thế giới bên ngoài. JSCH là một mớ hỗn độn của tài liệu xấu và thiết kế API với phụ thuộc ẩn và phần lớn không thể giải mã được. Trừ khi bạn muốn dành nhiều thời gian đi qua mã để cố gắng tìm ra những gì đang diễn ra, hãy sử dụng SSHJ. (Và tôi ước mình đã trở nên khắc nghiệt hoặc khéo léo về JSCH. Tôi thực sự làm.) –

+0

Có, sshj. Tất cả mọi thứ tôi đã thử với nó hoạt động: SCP, thực thi quá trình từ xa, chuyển tiếp cổng cục bộ và từ xa, proxy đại lý với [jsch-agent-proxy] (http://www.jcraft.com/jsch-agent-proxy). JSCH là một mớ hỗn độn. –

5

Có một phiên bản hoàn toàn mới của Jsch trên github: https://github.com/vngx/vngx-jsch Một số cải tiến bao gồm: javadoc toàn diện, hiệu suất nâng cao, cải thiện xử lý ngoại lệ và tuân thủ đặc tính RFC tốt hơn. Nếu bạn muốn đóng góp bằng bất kỳ cách nào, vui lòng mở một vấn đề hoặc gửi yêu cầu kéo.

+2

Quá tệ, chưa có cam kết mới trong hơn 3 năm nay. –

0

Tôi lấy câu trả lời của miku và mã ví dụ. Sau đó, tôi đã phải tải xuống nhiều tệp trong phiên và giữ lại dấu thời gian ban đầu. Đây là mã ví dụ của tôi làm thế nào để làm điều đó, có lẽ nhiều người tìm thấy nó hữu ích. Xin vui lòng bỏ qua filenameHack() chức năng usecase của riêng tôi.

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

Bạn có thể sử dụng lại phiên làm việc và tránh được phí kết nối/ngắt kết nối không? –

0

http://code.google.com/p/connectbot/, Biên dịch src \ com \ trilead \ SSH2 trên cửa sổ linux hoặc android, nó có thể tạo địa phương Cảng Forwarder hoặc tạo động Cảng Forwarder hoặc khác khác