1.JSch介绍
JSch是SSH2的纯Java实现。
JSch允许您连接到sshd服务器并使用端口转发,X11转发,文件传输等,并且可以将其功能集成到您自己的Java程序中。
2.实现原理
- 根据远程主机的IP地址,用户名和端口,建立会话(Session)
- 设置用户信息(包括密码和Userinfo),然后连接session,getSession()只是创建一个session,需要设置必要的认证信息之后,调用connect()才能建立连接。
- 设置channel上需要远程执行的Shell脚本,连接channel,就可以远程执行该Shell脚本,调用openChannel(String type) 可以在session上打开指定类型的channel。该channel只是被初始化,使用前需要先调用connect()进行连接。
- 可以读取远程执行Shell脚本的输出,然后依次断开channel和session的连接
3.代码工程
实验目标:实现文件上传到服务,服务器下载文件以及执行服务器命令
pom.xml
xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springboot-demo</artifactId>
<groupId>com.et</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>JSch</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/com.jcraft/jsch -->
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.55</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.78</version>
</dependency>
</dependencies>
</project>
remote.java
arduino
package com.et.jsch.model;
import lombok.Data;
@Data
public class Remote {
private String host;
private final int port = 22;
private String user;
private String password;
private final String identity = "~/.ssh/id_rsa";
private String passphrase;
}
JSchUtil.java
ini
package com.et.jsch.util;
import com.et.jsch.model.Remote;
import com.jcraft.jsch.*;
import lombok.extern.slf4j.Slf4j;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
/**
* ssh tools
*/
@Slf4j
public class JSchUtil {
public static final int SESSION_TIMEOUT = 30000;
public static final int CONNECT_TIMEOUT = 3000;
/**
* get session
*
* @param remote ssh server info
* @return session
* @throws JSchException /
*/
public static Session getSession(Remote remote) throws JSchException {
JSch jSch = new JSch();
if (Files.exists(Paths.get(remote.getIdentity()))) {
jSch.addIdentity(remote.getIdentity(), remote.getPassphrase());
}
Session session = jSch.getSession(remote.getUser(), remote.getHost(), remote.getPort());
session.setPassword(remote.getPassword());
session.setConfig("StrictHostKeyChecking", "no");
return session;
}
/**
* excute remote command
*
* @param session session
* @param command command
* @return /
* @throws JSchException /
*/
public static List<String> remoteExecute(Session session, String command) throws JSchException {
log.debug(">> {}", command);
List<String> resultLines = new ArrayList<>();
ChannelExec channel = null;
try {
channel = openExecChannel(session);
channel.setCommand(command);
InputStream input = channel.getInputStream();
channel.connect(CONNECT_TIMEOUT);
try {
BufferedReader inputReader = new BufferedReader(new InputStreamReader(input));
String inputLine;
while ((inputLine = inputReader.readLine()) != null) {
log.debug(" {}", inputLine);
resultLines.add(inputLine);
}
} finally {
if (input != null) {
try {
input.close();
} catch (Exception e) {
log.error("JSch inputStream close error:", e);
}
}
}
} catch (IOException e) {
log.error("IOException:", e);
} finally {
disconnect(channel);
}
return resultLines;
}
/**
* scp file to remote server
*
* @param session session
* @param source local file
* @param destination remote target file
* @return file size
*/
public static long scpTo(Session session, String source, String destination) {
FileInputStream fileInputStream = null;
ChannelExec channel = null;
try {
channel = openExecChannel(session);
OutputStream out = channel.getOutputStream();
InputStream in = channel.getInputStream();
boolean ptimestamp = false;
String command = "scp";
if (ptimestamp) {
command += " -p";
}
command += " -t " + destination;
channel.setCommand(command);
channel.connect(CONNECT_TIMEOUT);
if (checkAck(in) != 0) {
return -1;
}
File _lfile = new File(source);
if (ptimestamp) {
command = "T " + (_lfile.lastModified() / 1000) + " 0";
// The access time should be sent here,
// but it is not accessible with JavaAPI ;-<
command += (" " + (_lfile.lastModified() / 1000) + " 0\n");
out.write(command.getBytes());
out.flush();
if (checkAck(in) != 0) {
return -1;
}
}
//send "C0644 filesize filename", where filename should not include '/'
long fileSize = _lfile.length();
command = "C0644 " + fileSize + " ";
if (source.lastIndexOf('/') > 0) {
command += source.substring(source.lastIndexOf('/') + 1);
} else {
command += source;
}
command += "\n";
out.write(command.getBytes());
out.flush();
if (checkAck(in) != 0) {
return -1;
}
//send content of file
fileInputStream = new FileInputStream(source);
byte[] buf = new byte[1024];
long sum = 0;
while (true) {
int len = fileInputStream.read(buf, 0, buf.length);
if (len <= 0) {
break;
}
out.write(buf, 0, len);
sum += len;
}
//send '\0'
buf[0] = 0;
out.write(buf, 0, 1);
out.flush();
if (checkAck(in) != 0) {
return -1;
}
return sum;
} catch (JSchException e) {
log.error("scp to caught jsch exception, ", e);
} catch (IOException e) {
log.error("scp to caught io exception, ", e);
} catch (Exception e) {
log.error("scp to error, ", e);
} finally {
closeInputStream(fileInputStream);
disconnect(channel);
}
return -1;
}
/**
* scp remote file to local
*
* @param session session
* @param source remote file
* @param destination local file
* @return file size
*/
public static long scpFrom(Session session, String source, String destination) {
FileOutputStream fileOutputStream = null;
ChannelExec channel = null;
try {
channel = openExecChannel(session);
channel.setCommand("scp -f " + source);
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) {
if (checkAck(in) != 'C') {
break;
}
}
//read '644 '
in.read(buf, 0, 4);
long fileSize = 0;
while (true) {
if (in.read(buf, 0, 1) < 0) {
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
if (Files.isDirectory(Paths.get(destination))) {
fileOutputStream = new FileOutputStream(destination + File.separator + file);
} else {
fileOutputStream = new FileOutputStream(destination);
}
long sum = 0;
while (true) {
int len = in.read(buf, 0, buf.length);
if (len <= 0) {
break;
}
sum += len;
if (len >= fileSize) {
fileOutputStream.write(buf, 0, (int) fileSize);
break;
}
fileOutputStream.write(buf, 0, len);
fileSize -= len;
}
return sum;
} catch (JSchException e) {
log.error("scp to caught jsch exception, ", e);
} catch (IOException e) {
log.error("scp to caught io exception, ", e);
} catch (Exception e) {
log.error("scp to error, ", e);
} finally {
closeOutputStream(fileOutputStream);
disconnect(channel);
}
return -1;
}
/**
* remote edit
*
* @param session session
* @param source target file
* @param process edit command collect
* @return isSuccess
*/
private static boolean remoteEdit(Session session, String source, Function<List<String>, List<String>> process) {
InputStream in = null;
OutputStream out = null;
try {
String fileName = source;
int index = source.lastIndexOf('/');
if (index >= 0) {
fileName = source.substring(index + 1);
}
//backup source
remoteExecute(session, String.format("cp %s %s", source, source + ".bak." + System.currentTimeMillis()));
//scp from remote
String tmpSource = System.getProperty("java.io.tmpdir") + session.getHost() + "-" + fileName;
scpFrom(session, source, tmpSource);
in = new FileInputStream(tmpSource);
//edit file according function process
String tmpDestination = tmpSource + ".des";
out = new FileOutputStream(tmpDestination);
List<String> inputLines = new ArrayList<>();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
String inputLine = null;
while ((inputLine = reader.readLine()) != null) {
inputLines.add(inputLine);
}
List<String> outputLines = process.apply(inputLines);
for (String outputLine : outputLines) {
out.write((outputLine + "\n").getBytes());
out.flush();
}
//scp to remote
scpTo(session, tmpDestination, source);
return true;
} catch (Exception e) {
log.error("remote edit error, ", e);
return false;
} finally {
closeInputStream(in);
closeOutputStream(out);
}
}
/**
* update file
*
* @param session session
* @param in file stream
* @param directory local dir
* @param fileName FTP server file name:xxx.txt ||xxx.txt.zip
*/
public static boolean uploadFile(Session session, InputStream in, String directory, String fileName) {
log.info(">>>>>>>>uploadFile--ftp start>>>>>>>>>>>>>");
ChannelSftp channel = null;
try {
channel = openSftpChannel(session);
channel.connect(CONNECT_TIMEOUT);
String[] folders = directory.split("/");
try {
for (int i = 0; i < folders.length; i++) {
if (i == 0 && folders[i].length() == 0) {
channel.cd("/");
} else if (folders[i].length() > 0) {
try {
channel.cd(folders[i]);
} catch (SftpException e) {
channel.mkdir(folders[i]);
channel.cd(folders[i]);
}
}
}
} catch (SftpException e) {
log.error("ftp create file fail" + directory, e);
return false;
}
try {
channel.put(in, fileName);
} catch (SftpException e) {
log.error("sftp error-->" + e.getMessage(), e);
return false;
}
log.info(">>>>>>>>uploadFile--ftp upload end>>>>>>>>>>>>>");
log.info(">>>>>>>>ftp upload dir:{},filename:{}>>>>>>>>>>>>>", directory, fileName);
return true;
} catch (JSchException e) {
log.error("JSch error-->" + e.getMessage(), e);
return false;
} finally {
closeInputStream(in);
disconnect(channel);
}
}
/**
*
*
* @param channel sftp connect
* @param directory
* @param fileName
* @return
*/
public static InputStream stream(ChannelSftp channel, String directory, String fileName) {
try {
channel.connect(CONNECT_TIMEOUT);
InputStream inputStream = channel.get(directory + "/" + fileName);
log.info(">>>>>>>>ftp file directory:{},filename:{}>>>>>>>>>>>>>", directory, fileName);
return inputStream;
} catch (SftpException e) {
log.error("sftp error-->" + e.getMessage());
return null;
} catch (JSchException e) {
log.error("JSch error-->" + e.getMessage());
return null;
}
}
/**
* ftp delete remote file
*
* @param session session
* @param directory directory
* @param fileName filename
* @return is Success
*/
public static boolean deleteFile(Session session, String directory, String fileName) {
log.info(">>>>>>>>deleteFile--ftp delete file end>>>>>>>>>>>>>");
ChannelSftp channel = null;
try {
channel = openSftpChannel(session);
channel.connect(CONNECT_TIMEOUT);
channel.rm(directory + "/" + fileName);
log.info(">>>>>>>>deleteFile--deletefile end>>>>>>>>>>>>>");
log.info(">>>>>>>>ftp delete file directory:{},filename:{}>>>>>>>>>>>>>", directory, fileName);
} catch (SftpException e) {
log.error("ftp create directory fail" + directory);
return false;
} catch (JSchException e) {
log.error("JSch error-->" + e.getMessage());
return false;
} finally {
disconnect(channel);
}
return true;
}
public static Channel openChannel(Session session, String type) throws JSchException {
if (!session.isConnected()) {
session.connect(SESSION_TIMEOUT);
}
return session.openChannel(type);
}
public static ChannelSftp openSftpChannel(Session session) throws JSchException {
return (ChannelSftp) openChannel(session, "sftp");
}
public static ChannelExec openExecChannel(Session session) throws JSchException {
return (ChannelExec) openChannel(session, "exec");
}
/**
* disconnect
*
* @param session
*/
public static void disconnect(Session session) {
if (session != null) {
if (session.isConnected()) {
try {
session.disconnect();
log.info("session disconnect successfully");
} catch (Exception e) {
log.error("JSch session disconnect error:", e);
}
}
}
}
/**
* close connection
*
* @param channel channel connection
*/
public static void disconnect(Channel channel) {
if (channel != null) {
if (channel.isConnected()) {
try {
channel.disconnect();
log.info("channel is closed already");
} catch (Exception e) {
log.error("JSch channel disconnect error:", e);
}
}
}
}
public static int checkAck(InputStream in) throws IOException {
int b = in.read();
// b may be 0 for success,
// 1 for error,
// 2 for fatal error,
// -1
if (b == 0) {
return b;
}
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');
if (b == 1) { // error
log.debug(sb.toString());
}
if (b == 2) { // fatal error
log.debug(sb.toString());
}
}
return b;
}
public static void closeInputStream(InputStream in) {
if (in != null) {
try {
in.close();
} catch (IOException e) {
log.error("Close input stream error." + e.getMessage());
}
}
}
public static void closeOutputStream(OutputStream out) {
if (out != null) {
try {
out.close();
} catch (IOException e) {
log.error("Close output stream error." + e.getMessage());
}
}
}
}
DemoApplication.java
typescript
package com.et.jsch;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
代码仓库
4.测试
ini
import com.alibaba.fastjson.JSONObject;
import com.et.jsch.DemoApplication;
import com.et.jsch.model.Remote;
import com.et.jsch.util.JSchUtil;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.List;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = DemoApplication.class)
public class JSchUtilTests {
private Logger log = LoggerFactory.getLogger(getClass());
Session session;
@Before
public void before() throws JSchException {
Remote remote= new Remote();
remote.setHost("xxx.xxx.xxx.xxx");
remote.setUser("root");
remote.setPassword("xxxx");
session= JSchUtil.getSession(remote);
}
@After
public void after(){
JSchUtil.disconnect(session);
}
@Test
public void remoteExecute() throws JSchException {
List<String> list= JSchUtil.remoteExecute(session,"ls");
System.out.println(JSONObject.toJSON(list));
}
@Test
public void uploadFile() throws JSchException, FileNotFoundException {
String filestr ="D:\\tmp\\test\\file_utils\\file1.txt";
File file = new File(filestr);
InputStream in = new FileInputStream(file);
String directory="/root/test";
String fileName="test.txt";
boolean flag= JSchUtil.uploadFile(session,in,directory,fileName);
System.out.println(flag);
}
@Test
public void deleteFile() throws JSchException, FileNotFoundException {
String directory="/root/test";
String fileName="test.txt";
boolean flag= JSchUtil.deleteFile(session,directory,fileName);
System.out.println(flag);
}
@Test
public void scpFrom() throws JSchException, FileNotFoundException {
String source="/root/test/file1.txt";
String destination ="D:\\tmp\\scfFrom.txt";
long filesize= JSchUtil.scpFrom(session,source,destination);
System.out.println(filesize);
}
@Test
public void scpTo() throws JSchException, FileNotFoundException {
String filestr ="D:\\tmp\\test\\file_utils\\file1.txt";
String destination="/root/test/file1.txt";
long filesize= JSchUtil.scpTo(session,filestr,destination);
System.out.println(filesize);
}
}
自己尝试一下吧,非常好的一个工具,更多功能可以去官网看例子