Pustaka SSH untuk Java [ditutup]


190

Adakah yang tahu perpustakaan yang bagus untuk login SSH dari Java.


Saya dulu menggunakan Trilead SSH tetapi ketika saya memeriksa situs hari ini sepertinya mereka menyerah. :( Itu yang paling favorit saya.
Peter D

1
BTW, tampaknya Trilead SSH2 sedang dipelihara secara aktif (pada Oktober 2013): [ github.com/jenkinsci/trilead-ssh2]
Mike Godin

Trilead SSH2 memiliki garpu di github.com/connectbot/sshlib
user7610

Jawaban:


120

The Java Aman Channel (jsch) adalah perpustakaan yang sangat populer, digunakan oleh pakar, semut dan gerhana. Ini adalah sumber terbuka dengan lisensi gaya BSD.


2
Anda dapat mengunduh sumber dari sourceforge.net/projects/jsch/files/jsch/jsch-0.1.42.zip/… dan menjalankan "ant javadoc"
David Rabinowitz

73
Saya sudah mencoba menggunakan JSch beberapa waktu yang lalu dan tidak dapat memahami bagaimana ini menjadi sangat populer. Ia sama sekali tidak menawarkan dokumentasi (bahkan di dalam sumbernya) dan desain API yang mengerikan ( techtavern.wordpress.com/2008/09/30/… merangkum dengan cukup baik)
rluba

15
Ya Jsch mengerikan, ini lebih baik: github.com/shikhar/sshj
anio

3
Varian dari JSch dengan javadoc untuk metode publik: github.com/ePaul/jsch-documentation
user423430

4
stackoverflow.com/questions/2405885/any-good-jsch-examples/… berisi contoh untuk menggunakan JSCH untuk menjalankan perintah dan mendapatkan output.
Charity Leschinski

65

Pembaruan: Proyek GSOC dan kode di sana tidak aktif, tetapi ini adalah: https://github.com/hierynomus/sshj

hierynomus mengambil alih sebagai pengelola sejak awal 2015. Inilah tautan Github yang lebih lama, tidak lagi dipertahankan:

https://github.com/shikhar/sshj


Ada proyek GSOC:

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

Kualitas kode tampaknya lebih baik daripada JSch, yang, sementara implementasi yang lengkap dan berfungsi, tidak memiliki dokumentasi. Halaman proyek melihat rilis beta yang akan datang, komit terakhir ke repositori adalah pertengahan Agustus.

Bandingkan 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
Terima kasih! Saya menggunakan kode Apache SSHD (yang menawarkan async API) sebagai seed yang memberikan proyek kickstart.
shikhar

1
Bagus. Saya memulai proyek menggunakan JSch, tetapi saya sangat suka berganti, jika saya mendengar lebih banyak umpan balik positif tentang commons-net-ssh.
miku

5
Aku seharusnya menyebutkan bahwa saya adalah mahasiswa GSOC :)
shikhar

2
dengan senang hati mengumumkan github.com/shikhar/sshj - Anda dapat menemukan toples di sana, mencari tahu bagaimana cara mendapatkannya di repo pakar
shikhar

1
SFTP jsch jauh lebih sederhana dari apa yang diberikan di sini. Mungkin ini adalah kode lama, tetapi tentu saja ini bukan contoh dari API modern.
Charles Duffy

24

Saya baru saja menemukan sshj , yang tampaknya memiliki API yang jauh lebih ringkas daripada JSCH (tetapi membutuhkan Java 6). Dokumentasi ini sebagian besar dengan contoh-contoh pada saat ini, dan biasanya itu sudah cukup bagi saya untuk mencari di tempat lain, tetapi tampaknya cukup baik bagi saya untuk mencobanya di proyek yang baru saya mulai.


3
SSHJ sebenarnya bisa dilakukan oleh seseorang dari dunia luar. JSCH adalah kekacauan dokumentasi dan desain API yang buruk dengan dependensi tersembunyi dan sebagian besar tidak dapat dipahami. Kecuali Anda ingin menghabiskan banyak waktu berjalan melalui kode untuk mencoba mencari tahu apa yang terjadi, gunakan SSHJ. (Dan saya berharap saya bersikap keras atau ceroboh tentang JSCH. Saya benar-benar melakukannya.)
Robert Fischer

1
Ya, sshj. Semua yang saya coba dengannya berfungsi: SCP, eksekusi proses jarak jauh, penerusan porta lokal dan jarak jauh, proxy agen dengan jsch-agent-proxy . JSCH berantakan.
Laurent Caillette

1
Masalah dengan SSHJ adalah sangat sulit untuk mengeksekusi banyak perintah. SSHJ mungkin bagus untuk perintah api-dan-lupa, tetapi itu menyebalkan jika Anda ingin memprogram interaksi yang lebih rumit. (Saya hanya menghabiskan setengah hari di atasnya)
bvdb

18

Lihatlah SSHD yang baru-baru ini dirilis , yang didasarkan pada proyek Apache MINA.


2
Menggunakannya, namun ada kekurangan dokumentasi dan contoh.
Andreas Mattisson

Sepertinya kurangnya dokumentasi membuatnya enteng: /
Amalgovinus

5

Ada versi baru Jsch di github: https://github.com/vngx/vngx-jsch Beberapa peningkatan termasuk: javadoc komprehensif, peningkatan kinerja, penanganan pengecualian yang ditingkatkan, dan kepatuhan spesifikasi RFC yang lebih baik. Jika Anda ingin berkontribusi dengan cara apa pun, silakan buka masalah atau kirim permintaan penarikan.


4
Sayang sekali belum ada komitmen baru dalam lebih dari 3 tahun sekarang.
Mike Lowery

0

Saya mengambil jawaban miku dan kode contoh jsch. Saya kemudian harus mengunduh banyak file selama sesi dan menjaga cap waktu asli . Ini adalah contoh kode saya bagaimana melakukannya, mungkin banyak orang merasa berguna. Harap abaikan namafileHack () berfungsi sebagai usecase saya sendiri.

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

}

Apakah Anda dapat menggunakan kembali sesi dan menghindari overhead menghubungkan / memutuskan sambungan?
Sridhar Sarnobat

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.