SSH-Bibliothek für Java [geschlossen]


187

Kennt jemand eine gute Bibliothek für die SSH-Anmeldung von Java.


Früher habe ich Trilead SSH verwendet, aber als ich heute auf der Website nachgesehen habe, scheinen sie es aufzugeben. :( Es war mein absoluter Favorit.
Peter D

1
Übrigens scheint Trilead SSH2 aktiv gewartet zu werden (im Oktober 2013): [ github.com/jenkinsci/trilead-ssh2]
Mike Godin

Trilead SSH2 hat eine Gabelung unter github.com/connectbot/sshlib
user7610

Antworten:


120

Der Java Secure Channel (JSCH) ist eine sehr beliebte Bibliothek, die von Maven, Ant und Eclipse verwendet wird. Es ist Open Source mit einer Lizenz im BSD-Stil.


2
Sie müssen die Quelle von sourceforge.net/projects/jsch/files/jsch/jsch-0.1.42.zip/… herunterladen und "ant javadoc"
David Rabinowitz

73
Ich habe vor einiger Zeit versucht, JSch zu verwenden, und kann nicht verstehen, wie es so populär wurde. Es bietet absolut keine Dokumentation (nicht einmal innerhalb der Quelle) und ein schreckliches API-Design ( techtavern.wordpress.com/2008/09/30/… fasst es ziemlich gut zusammen)
Rluba

15
Ja, Jsch ist schrecklich, das ist besser: github.com/shikhar/sshj
anio

3
Eine Variante von JSch mit Javadoc für die öffentlichen Methoden: github.com/ePaul/jsch-documentation
user423430

4
stackoverflow.com/questions/2405885/any-good-jsch-examples/… enthält ein Beispiel für die Verwendung von JSCH zum Ausführen von Befehlen und zum Abrufen der Ausgabe.
Charity Leschinski

65

Update: Das GSOC-Projekt und der Code dort sind nicht aktiv, aber dies ist: https://github.com/hierynomus/sshj

Hierynomus übernahm seit Anfang 2015 die Leitung. Hier ist der ältere, nicht mehr gepflegte Github-Link:

https://github.com/shikhar/sshj


Es gab ein GSOC-Projekt:

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

Die Codequalität scheint besser zu sein als bei JSch, das zwar eine vollständige und funktionierende Implementierung aufweist, jedoch keine Dokumentation enthält. Auf der Projektseite wird eine bevorstehende Beta-Version veröffentlicht. Das letzte Commit für das Repository war Mitte August.

Vergleichen Sie die APIs:

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
Vielen Dank! Ich habe Apache SSHD-Code (der eine asynchrone API bietet) als Startwert verwendet, der dem Projekt einen Kickstart verlieh.
Shikhar

1
Toll. Ich habe ein Projekt mit JSch gestartet, aber ich wechsle sehr gerne, wenn ich mehr positives Feedback zu commons-net-ssh höre.
Miku

5
Ich hätte erwähnen sollen, dass ich der GSOC-Student war :)
Shikhar

2
glücklich , zu verkünden github.com/shikhar/sshj - Sie jar finden können es, wie es auf einer Maven - Repo zu bekommen , herauszufinden
shikhar

1
jschs SFTP ist viel einfacher als das, was hier angegeben ist. Vielleicht ist dies alter Code, aber es ist sicherlich kein Beispiel für die moderne API.
Charles Duffy

24

Ich habe gerade sshj entdeckt , das eine viel präzisere API als JSCH zu haben scheint (aber es erfordert Java 6). Die Dokumentation besteht zu diesem Zeitpunkt hauptsächlich aus Beispielen im Repo, und normalerweise reicht das aus, um mich anderswo umzusehen, aber es scheint gut genug zu sein, um es mit einem Projekt zu versuchen, das ich gerade begonnen habe.


3
SSHJ kann tatsächlich von jemandem aus der Außenwelt bearbeitet werden. JSCH ist ein Durcheinander von schlechter Dokumentation und API-Design mit versteckten und weitgehend nicht entzifferbaren Abhängigkeiten. Verwenden Sie SSHJ, es sei denn, Sie möchten viel Zeit damit verbringen, durch den Code zu gehen, um herauszufinden, was los ist. (Und ich wünschte, ich wäre hart oder scherzhaft gegenüber JSCH. Das tue ich wirklich.)
Robert Fischer

1
Ja, sshj. Alles, was ich damit versucht habe, hat funktioniert: SCP, Remote-Prozessausführung, lokale und Remote-Port-Weiterleitung, Agent-Proxy mit jsch-agent-proxy . JSCH war ein Chaos.
Laurent Caillette

1
Das Problem mit SSHJ ist, dass es sehr schwierig ist, mehrere Befehle auszuführen. SSHJ eignet sich zwar hervorragend zum Feuern und Vergessen von Befehlen, ist jedoch ein Problem, wenn Sie eine kompliziertere Interaktion programmieren möchten. (Ich habe gerade einen halben Tag damit verschwendet)
bvdb

18

Schauen Sie sich die kürzlich veröffentlichte SSHD an , die auf dem Apache MINA-Projekt basiert.


2
Bei der Verwendung fehlen jedoch Dokumentation und Beispiele.
Andreas Mattisson

Es sieht so aus, als würde ein Mangel an Dokumentation es leicht machen: /
Amalgovinus

5

Es gibt eine brandneue Version von Jsch auf github: https://github.com/vngx/vngx-jsch Einige der Verbesserungen umfassen: umfassendes Javadoc, verbesserte Leistung, verbesserte Ausnahmebehandlung und bessere Einhaltung von RFC-Spezifikationen. Wenn Sie in irgendeiner Weise einen Beitrag leisten möchten, öffnen Sie bitte eine Ausgabe oder senden Sie eine Pull-Anfrage.


4
Schade, dass es seit über 3 Jahren kein neues Commit mehr gibt.
Mike Lowery

0

Ich nahm Mikus Antwort und jsch Beispielcode. Ich musste dann während der Sitzung mehrere Dateien herunterladen und die ursprünglichen Zeitstempel beibehalten . Dies ist mein Beispielcode, wie es geht, wahrscheinlich finden es viele Leute nützlich. Bitte ignorieren Sie die Funktion filenameHack () in meinem eigenen Fall.

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

}

Konnten Sie eine Sitzung wiederverwenden und den Aufwand für das Verbinden / Trennen vermeiden?
Sridhar Sarnobat

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.