Czy ktoś wie o dobrej bibliotece do logowania SSH z Java.Biblioteka SSH dla Java
Odpowiedz
Java Secure Channel (JSCH) jest bardzo popularną biblioteką, używaną przez maven, mrówkę i zaćmienie. Jest to open source z licencją w stylu BSD.
To wygląda dobrze - czy są tam jakieś javadocs? wszystko?Przykłady są nieco słabo napisanym kodem huśtawki (z krótkiego samplowania, które zrobiłem). –
Chcesz pobrać źródło z https://sourceforge.net/projects/jsch/files/jsch/jsch-0.1.42.zip/download i uruchomić "ant javadoc" –
Próbowałem używać JSch jakiś czas temu i nie rozumiem, jak to się stało tak popularne. Nie oferuje absolutnie żadnej dokumentacji (nawet w źródle) i okropnego projektu API (http://techtavern.wordpress.com/2008/09/30/about-jsch-open-source-project/ podsumowuje to całkiem dobrze) – rluba
Spójrz na niedawno wydany SSHD, który jest oparty na projekcie Apache MINA.
Używając go, brakuje jednak dokumentacji i przykładów. –
Aktualizacja: Projekt GSoC a kod nie jest aktywny, ale to jest: https://github.com/hierynomus/sshj
hierynomus objął stanowisko opiekuna od początku 2015. Oto starsza, nie jest już rozwijany, Github Link:
https://github.com/shikhar/sshj
było projekt GSoC:
http://code.google.com/p/commons-net-ssh/
Jakość kodu wydaje się lepsza niż JSch, który, mimo że jest kompletny i działający, brakuje dokumentacji. Strona projektu dostrzega nadchodzące wydanie beta, ostatnie zatwierdzenie do repozytorium miało miejsce w połowie sierpnia.
Porównaj 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();
}
}
Dzięki! Użyłem kodu Apache SSHD (który oferuje async API) jako seed, który dał projektowi kickstart. – shikhar
Świetnie. Zacząłem projekt używając JSch, ale bardzo lubię się przełączać, gdy słyszę więcej pozytywnych opinii na temat commons-net-ssh. – miku
Powinienem wspomnieć, że byłem uczniem GSOC :) – shikhar
Właśnie odkryłem sshj, który wydaje się mieć dużo bardziej zwięzły API niż jsch (ale to wymaga Java 6). Dokumentacja jest w większości przypadków przykładowa w repozytorium i zazwyczaj wystarcza mi to, aby zajrzeć gdzie indziej, ale wydaje mi się, że wystarczy, że zrobię z tego projekt, który właśnie rozpocząłem.
SSHJ jest faktycznie możliwe do zrealizowania przez kogoś ze świata zewnętrznego. JSCH to bałagan złej dokumentacji i projektowania interfejsu API z ukrytymi i w dużej mierze nieczytelnymi zależnościami. Jeśli nie chcesz spędzać dużo czasu na przechodzeniu kodu, aby dowiedzieć się, co jest grane, użyj SSHJ. (I szkoda, że nie byłem szorstki lub kpiący z JSCH.) Naprawdę.) –
Tak, sshj. Wszystko, co wypróbowałem, działało: SCP, zdalne wykonywanie procesu, lokalne i zdalne przekazywanie portów, agent proxy z [jsch-agent-proxy] (http://www.jcraft.com/jsch-agent-proxy). JSCH był bałagan. –
Istnieje nowa wersja Jscha na github: https://github.com/vngx/vngx-jsch Niektóre ulepszenia obejmują: wszechstronny javadoc, zwiększoną wydajność, ulepszoną obsługę wyjątków i lepszą zgodność specyfikacji RFC. Jeśli chcesz w jakikolwiek sposób wnieść swój wkład, proszę otworzyć problem lub wysłać żądanie wyciągnięcia.
Szkoda, że nie było nowego zatwierdzenia od ponad 3 lat. –
Wziąłem odpowiedź miku i kod przykładowy jsch. Następnie musiałem pobrać wiele plików podczas sesji i zachować oryginalne znaczniki czasu. To jest mój przykładowy kod, jak to zrobić, pewnie wiele osób uzna to za przydatne. Proszę zignorować funkcję filenameHack(), która jest moją własną aplikacją.
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;
}
}
Czy byłeś w stanie ponownie wykorzystać sesję i uniknąć narzutów związanych z łączeniem/rozłączaniem? –
http://code.google.com/p/connectbot/, Kompilacja src \ com \ trilead \ SSH2 na Windows Linux lub Android, może tworzyć Local Port Forwarder lub tworzyć dynamiczne Port Forwarder lub inny inny
Ja używałem Trilead SSH ale gdy sprawdziłem Witryna dzisiaj wydaje się, że rezygnują z niej. :(To był mój absolutny ulubiony: –
BTW, wygląda na to, że Trilead SSH2 jest aktywnie utrzymywany (w październiku 2013 r.): [Https://github.com/jenkinsci/trilead-ssh2] –
Spróbuj [jcabi-ssh] (http://ssh.jcabi.com), wyjaśniono tutaj http://www.yegor256.com/2014/09/02/java-ssh-client.html – yegor256