Java用SSH连接服务器,原来这么简单
前言前段时间,接了个需求,需要在项目中ssh连接远程服务器,并执行一个脚本。那最重要的就是要找到一个可以在Java程序支持SSH的工具,然后就找到了JSch 。还有一个是SSH2,不过我连接没成功。JSch本文只是JSch的一些简单用法,想了解更多的,可以去官网:链接: http://www.jcraft.com/jsch/.以下是JSch 官方的介绍JSch 是SSH2的一个纯Java实现。它允
前言
前段时间,接了个需求,需要在项目中ssh连接远程服务器,并执行一个脚本。那最重要的就是要找到一个可以在Java程序支持SSH的工具,然后就找到了JSch 。还有一个是SSH2,不过我连接没成功。
JSch
本文只是JSch的一些简单用法,想了解更多的,可以去官网:
链接: http://www.jcraft.com/jsch/.
以下是JSch 官方的介绍
JSch 是SSH2的一个纯Java实现。它允许你连接到一个sshd 服务器,使用端口转发,X11转发,文件传输等等。你可以将它的功能集成到你自己的 Java程序中。JSch使用BSD风格许可证。
下面,我们就来使用JSch
1、添加依赖
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.54</version>
</dependency>
2、JSch 连接服务器
JSch 连接服务器主要步骤如下
1、创建jsch对象
2、创建session会话
3、设置session配置
4、使用connect方法进行连接
实现原理
1. 根据远程主机的IP地址,用户名和端口,建立会话(Session);
2. 设置用户信息(包括密码和Userinfo),然后连接session;
注:getSession()只是创建一个session,需要设置必要的认证信息之后,调用connect()才能建立连接。
3. 在session上建立指定类型的通道(Channel);
4. 设置channel上需要远程执行的Shell脚本,连接channel,就可以远程执行该Shell脚本;
注:使用channel前需要先调用connect()进行连接。
5. 读取远程执行Shell脚本的输出,然后依次断开channel和session的连接;
写了一个简单的例子,大家可以参考下
/**
* 连接服务器并执行shell命令
* @param host 主机ip
* @param port 端口
* @param userName 用户名
* @param password 密码
* @param cmd shell命令
* @return
*/
public List<String> execCmd(String host, int port, String userName, String password,String cmd){
// 创建jsch对象
JSch jsch = new JSch();
ChannelExec channelExec=null;
Session session=null;
InputStream inputStream=null;
// 输出结果到字符串数组
List<String> resultLines = new ArrayList<>();
// 创建session会话
try {
session = jsch.getSession(userName, host, port);
// 设置密码
session.setPassword(password);
// 创建一个session配置类
Properties sshConfig = new Properties();
// 跳过公钥检测
sshConfig.put("StrictHostKeyChecking", "no");
session.setConfig(sshConfig);
// 我们还可以设置timeout时间
session.setTimeout(SESSION_TIMEOUT);
// 建立连接
session.connect();
// session建立之后,我们就可以执行shell命令,或者上传下载文件了,下面我来执行shell命令
channelExec = (ChannelExec) session.openChannel("exec");
// 将shell传入command
channelExec.setCommand(cmd);
// 开始执行
channelExec.connect();
// 获取执行结果的输入流
inputStream = channelExec.getInputStream();
String result=null;
BufferedReader in = new BufferedReader(new InputStreamReader(inputStream));
while ((result = in.readLine()) != null) {
resultLines.add(result);
LOGGER.info("命令返回信息:{}", result);
}
}catch (Exception e){
LOGGER.error("Connect failed, {}", e.getMessage());
ArrayList<String> errorMsg = new ArrayList<>();
errorMsg.add(e.getMessage());
return errorMsg;
}finally {
// 释放资源
if (channelExec != null) {
try {
channelExec.disconnect();
} catch (Exception e) {
LOGGER.error("JSch channel disconnect error:", e);
}
}
if (session!=null){
try {
session.disconnect();
} catch (Exception e) {
LOGGER.error("JSch session disconnect error:", e);
}
}
if (inputStream!=null){
try {
inputStream.close();
} catch (Exception e) {
LOGGER.error("inputStream close error:", e);
}
}
}
return resultLines;
}
我输入”pwd”的shell命令,可以看到在控制台的日志打印出结果
Channel通道类型
JSch常用的Channel通道类型有三种,ChannelShell、ChannelExec、ChannelSftp
ChannelShell、ChannelExec是用于执行命令的
ChannelSftp是用于上传下载文件的
ChannelShell和ChannelExec的区别: 前者是交互式的,在channel.connect()之前,需要获取outputStream和inputStream,然后outputstream发送命令,从instream中读取命令的结果(注意,发送命令之后,读取命令之前要等待一会儿,一般需要写个循环判断,每秒读一次,根据实际情况设置xx秒超时退出),但是有个坏处是,它执行就像登陆到vm上打印的信息一样,无论执行命令后的任何信息,它都会通过instream返回到客户端来,而你可能仅仅需要命令执行后的结果;于是就有了后者,非交互的,一次通道执行一条命令。
上传和下载demo
/**
* 上传文件
* @param session session会话
* @param directory 上传的目录
* @param fileName 上传的文件名
* @param uploadFile 要上传的文件
*/
public boolean upload(Session session,String directory, String fileName, File uploadFile) {
FileInputStream fileInputStream=null;
ChannelSftp channelSftp=null;
try {
channelSftp= (ChannelSftp) session.openChannel("sftp");
channelSftp.connect();
LOGGER.info("start upload channel file!");
channelSftp.cd(directory);
fileInputStream = new FileInputStream(uploadFile);
channelSftp.put(fileInputStream, fileName);
return true;
} catch (Exception e) {
LOGGER.error("SFTPClient upload file failed, {}", e.getMessage(), e);
return false;
}finally {
if (fileInputStream != null) {
try {
fileInputStream.close();
} catch (Exception e) {
LOGGER.error("File output stream close error, ", e);
}
}
if (channelSftp!=null){
channelSftp.disconnect();
}
}
}
/**
* 下载文件
* @param session session会话
* @param directory 下载目录
* @param fileName 下载的文件名
* @param saveFile 存在本地的路径
*/
public File download(Session session,String directory, String fileName, String saveFile) {
ChannelSftp channelSftp=null;
FileOutputStream fileOutputStream=null;
try {
channelSftp= (ChannelSftp) session.openChannel("sftp");
channelSftp.connect();
channelSftp.cd(directory);
File file = new File(saveFile);
if (file.exists()) {
file.delete();
}
fileOutputStream = new FileOutputStream(file);
channelSftp.get(fileName, fileOutputStream);
return file;
} catch (Exception e) {
LOGGER.error("SFTPClient download file failed, {}", e.getMessage(), e);
return null;
}finally {
if (fileOutputStream != null) {
try {
fileOutputStream.close();
} catch (Exception e) {
LOGGER.error("File output stream close error, ", e);
}
}
if (channelSftp!=null){
channelSftp.disconnect();
}
}
}
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)