用于 Java 的 SSH 库

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/995944/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 22:01:10  来源:igfitidea点击:

SSH library for Java

javasshssh-tunnel

提问by rperez

Does anyone know of a good library for SSH login from Java.

有谁知道从 Java 进行 SSH 登录的好库。

采纳答案by David Rabinowitz

The Java Secure Channel (JSCH)is a very popular library, used by maven, ant and eclipse. It is open source with a BSD style license.

Java的安全通道(JSCH)是一种非常流行的库,Maven的,蚂蚁和Eclipse使用。它是开源的,具有 BSD 风格的许可证。

回答by mshafrir

Take a look at the very recently released SSHD, which is based on the Apache MINA project.

看看最近发布的SSHD,它基于 Apache MINA 项目。

回答by miku

Update: The GSOC project and the code there isn't active, but this is: https://github.com/hierynomus/sshj

更新:GSOC 项目和那里的代码没有激活,但这是:https: //github.com/hierynomus/sshj

hierynomus took over as maintainer since early 2015. Here is the older, no longer maintained, Github link:

hierynomus 从 2015 年初开始担任维护者。这是旧的,不再维护的 Github 链接:

https://github.com/shikhar/sshj

https://github.com/shikhar/sshj



There was a GSOC project:

有一个 GSOC 项目:

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

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

Code quality seem to be better than JSch, which, while a complete and working implementation, lacks documentation. Project page spots an upcoming beta release, last commit to the repository was mid-august.

代码质量似乎比 JSch 好,后者虽然是完整且有效的实现,但缺乏文档。项目页面显示了即将发布的 beta 版本,上次提交到存储库是在 8 月中旬。

Compare the APIs:

比较 API:

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

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/

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 '
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 '##代码##'

        // 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 '##代码##'

        // 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 '##代码##'

        // 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 '##代码##'
        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;
    }

}
' 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 '##代码##' 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 '##代码##' 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(); } }

回答by Ed Brannin

I just discovered sshj, which seems to have a much more concise API than JSCH (but it requires Java 6). The documentation is mostly by examples-in-the-repo at this point, and usually that's enough for me to look elsewhere, but it seems good enough for me to give it a shot on a project I just started.

我刚刚发现了sshj,它的 API 似乎比 JSCH 简洁得多(但它需要 Java 6)。在这一点上,文档主要是通过示例中的存储库编写的,通常这足以让我查看其他地方,但似乎足以让我尝试一下我刚刚开始的项目。

回答by Scott

There is a brand new version of Jsch up on github: https://github.com/vngx/vngx-jschSome of the improvements include: comprehensive javadoc, enhanced performance, improved exception handling, and better RFC spec adherence. If you wish to contribute in any way please open an issue or send a pull request.

github 上有一个全新版本的 Jsch:https: //github.com/vngx/vngx-jsch一些改进包括:全面的 javadoc、增强的性能、改进的异常处理和更好的 RFC 规范遵守。如果您希望以任何方式做出贡献,请打开一个问题或发送请求请求。

回答by Whome

I took miku's answer and jsch example code. I then had to download multiple filesduring the session and preserve original timestamps. This is my example code how to do it, probably many people find it usefull. Please ignore filenameHack() function its my own usecase.

我拿了 miku 的答案和 jsch 示例代码。然后我不得不在会话期间下载多个文件保留原始时间戳。这是我的示例代码如何做到这一点,可能很多人觉得它很有用。请忽略 filenameHack() 函数,这是我自己的用例。

##代码##

回答by a1241312

http://code.google.com/p/connectbot/, Compile src\com\trilead\ssh2 on windows linux or android , it can create Local Port Forwarder or create Dynamic Port Forwarder or other else

http://code.google.com/p/connectbot/, 在 windows linux 或 android 上编译 src\com\trilead\ssh2 ,它可以创建本地端口转发器或创建动态端口转发器或其他