用于Java的SSH库
有没有人知道从Java的SSHlogin一个很好的库。
Java Secure Channel(JSCH)是一个非常stream行的库,由maven,ant和eclipse使用。 它是开源的BSD风格的许可证。
更新:GSOC项目和代码不活跃,但是这是: https : //github.com/hierynomus/sshj
自2015年初以来,hierynomus成为了维护者。下面是Github链接中较旧的,不再维护的:
https://github.com/shikhar/sshj
有一个GSOC项目:
http://code.google.com/p/commons-net-ssh/
代码质量似乎比JSch更好,虽然这是一个完整的工作实现,但缺乏文档。 项目页面发布即将发布的beta版本,上次提交到版本库的时间是8月中旬。
比较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(); }
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(); } }
我刚刚发现sshj ,它似乎比JSCH有一个更简洁的API(但它需要Java 6)。 这个文档主要是通过这个例子来进行回购的,通常这足以让我去其他地方看看,但是对于我刚开始的一个项目来说,似乎已经足够了。
看看最近发布的基于Apache MINA项目的SSHD 。
在github上有一个全新的Jsch版本: https : //github.com/vngx/vngx-jsch一些改进包括:全面的javadoc,增强的性能,改进的exception处理以及更好的RFC规范遵从性。 如果您希望以任何方式提供帮助,请打开问题或发送拉取请求。
我采取了miku的答案和jsch示例代码。 然后,我必须在会话期间下载多个文件 ,并保留原始时间戳 。 这是我的示例代码如何做,可能很多人觉得它有用。 请忽略filenameHack()函数它自己的用例。
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; } }
http://code.google.com/p/connectbot/ ,在windows linux或android上编译src \ com \ trilead \ ssh2,它可以创build本地端口转发器或创builddynamic端口转发器或其他