不灭的焱

革命尚未成功,同志仍须努力下载JDK17

作者:php-note.com  发布于:2022-08-27 17:03  分类:Java框架/系统  编辑

第 1 章 JSch简介

1.1 简述

1)JSch是ssh2的一个纯Java实现。它允许你连接到一个sshd服务器,使用端口转发、X11转发、文件传输等。

2)SSH 是较可靠,专为远程登录会话和其他网络服务提供安全性的协议。

3)ftp协议通常是用来在两个服务器之间传输文件的,但是它本质上是不安全的。

4)SFTP可以理解为SSH + FTP,也就是安全的网络文件传输协议。

1.2 实现原理

Jsch进行服务器连接时可以看作java的jdbc连接,首先需要实例化一个jsch对象,再利用这个对象根据用户名,主机ip,端口获取一个Session对象,设置好相应的参数后,进行连接,创建连接后,这个session时一直可用的,所以不需要关闭。之后我们需要在session上建立channel通道。

1.3 JSch官网

http://www.jcraft.com/jsch/

1.4 导入依赖

<dependency>
  	<groupId>com.jcraft</groupId>
  	<artifactId>jsch</artifactId>
  	<version>0.1.55</version>
</dependency>

实际项目中,使用了下面这个jsch包: (参考链接:https://php-note.com/2275.html

<dependency>
    <groupId>com.github.mwiede</groupId>
    <artifactId>jsch</artifactId>
    <version>0.2.3</version>
</dependency>

1.5 Channel的常用类型

1)Channel的常用类型有 ChannelShell 、 ChannelExec 和 ChannelSftp

2)ChannelShell和ChannelExec区别:

​ ChannelShell可以看作是执行一个交互式的Shell,而ChannelExec是执行一个Shell脚本。

3)ChannelSftp对象实现文件上传下载,ChannelSftp类是Jsch实现SFTP核心类,它包含了所有SFTP的方法

方法 操作
put() 文件上传
get() 文件下载
cd() 进入指定目录
ls()  得到指定目录下的文件或目录
rename() 重命名指定文件或目录
rm() 删除指定文件
mkdir()  创建目录
rmdir() 删除目录

第 2 章 Demo

2.1 SSH连接

  1. # 说明
  2. 当前Demo可用作判断是否可以和该主机ip通信

JSchConnect.java

package com.ix.utils;

import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Properties;

public class JSchConnect {

    private static final Logger log = LoggerFactory.getLogger(JSchConnect.class);

    public static void main(String[] args) {

        String username = "root";
        String password = "123456";
        String host = "192.168.66.36";
        int port = 22;

        // 创建JSch对象
        JSch jsch = new JSch();
        Session session = null;
        boolean result = false;

        try {
            // 根据主机账号、ip、端口
            session = jsch.getSession(username, host, port);
            // 设置主机密码
            session.setPassword(password);

            // 去掉首次连接确认
            session.setConfig("StrictHostKeyChecking", "on");

            // 超时连接时间为3秒
            session.setTimeout(3000);

            // 进行连接
            session.connect();

            // 获取连接结果
            result = session.isConnected();

        } catch (JSchException e) {
            log.warn(e.getMessage());
        } finally {
            // 关闭session流
            if (session != null && session.isConnected()) {
                session.disconnect();
            }
        }

        if (result) {
            log.error("【SSH连接】连接成功");
        } else {
            log.error("【SSH连接】连接失败");
        }
    }
}

2.2 文件上传

Jsch支持三种文件传输模式

OVERWRITE 完全覆盖模式,JSch默认文件传输模式,如果目标文件已经存在,传输的文件将完全覆盖目标文件,产生新的文件
RESUME 恢复模式,如果文件已经传输一部分,这时由于网络或其他任何原因导致文件传输终端,如果下次传输相同的文件,则会从上次终端的地方继续上传
APPEND 追加模式,如果目标文件已经存在,传输的文件将在目标文件后追加。

实现文件上传可以调用ChannelSftp对象的put方法。ChannelSftp中有12个put方法的重载方法

public void put(String src, String dst) 将本地文件名为src的文件上传到目标服务器,目标文件名为dst,若dst为目录,则目标文件名将与src文件名相同。 采用默认的传输模式:OVERWRITE
public void put(String src, String dst, int mode) 将本地文件名为src的文件上传到目标服务器,目标文件名为dst,若dst为目录,则目标文件名将与src文件名相同。 指定文件传输模式为mode(mode可选值为:ChannelSftp.OVERWRITE,ChannelSftp.RESUME, ChannelSftp.APPEND)
public void put(String src, String dst, SftpProgressMonitor monitor) 将本地文件名为src的文件上传到目标服务器,目标文件名为dst,若dst为目录,则目标文件名将与src文件名相同。 采用默认的传输模式:OVERWRITE 并使用实现了SftpProgressMonitor接口的monitor对象来监控文件传输的进度。
public void put(String src, String dst, SftpProgressMonitor monitor, int mode) 将本地文件名为src的文件上传到目标服务器,目标文件名为dst,若dst为目录,则目标文件名将与src文件名相同。 指定传输模式为mode 并使用实现了SftpProgressMonitor接口的monitor对象来监控文件传输的进度。
public void put(InputStream src, String dst) 将本地的input stream对象src上传到目标服务器,目标文件名为dst,dst不能为目录。 采用默认的传输模式:OVERWRITE
public void put(InputStream src, String dst, int mode) 将本地的input stream对象src上传到目标服务器,目标文件名为dst,dst不能为目录。 指定文件传输模式为mode
public void put(InputStream src, String dst, SftpProgressMonitor monitor) 将本地的input stream对象src上传到目标服务器,目标文件名为dst,dst不能为目录。 采用默认的传输模式:OVERWRITE 并使用实现了SftpProgressMonitor接口的monitor对象来监控传输的进度。
public void put(InputStream src, String dst, SftpProgressMonitor monitor, int mode) 将本地的input stream对象src上传到目标服务器,目标文件名为dst,dst不能为目录。 指定文件传输模式为mode 并使用实现了SftpProgressMonitor接口的monitor对象来监控传输的进度。
public OutputStream put(String dst) 该方法返回一个输出流,可以向该输出流中写入数据,最终将数据传输到目标服务器,目标文件名为dst,dst不能为目录。 采用默认的传输模式:OVERWRITE
public OutputStream put(String dst, final int mode) 该方法返回一个输出流,可以向该输出流中写入数据,最终将数据传输到目标服务器,目标文件名为dst,dst不能为目录。 指定文件传输模式为mode
public OutputStream put(String dst, final SftpProgressMonitor monitor, final int mode) 该方法返回一个输出流,可以向该输出流中写入数据,最终将数据传输到目标服务器,目标文件名为dst,dst不能为目录。 指定文件传输模式为mode 并使用实现了SftpProgressMonitor接口的monitor对象来监控传输的进度。
public OutputStream put(String dst, final SftpProgressMonitor monitor, final int mode, long offset) 该方法返回一个输出流,可以向该输出流中写入数据,最终将数据传输到目标服务器,目标文件名为dst,dst不能为目录。 指定文件传输模式为mode 并使用实现了SftpProgressMonitor接口的monitor对象来监控传输的进度。 offset指定了一个偏移量,从输出流偏移offset开始写入数据。
package com.ix.utils;

import com.jcraft.jsch.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Properties;

public class JSchUpload {

    private static final Logger log = LoggerFactory.getLogger(JSchUpload.class);

    public static void main(String[] args) {
        String username = "root";
        String password = "123456";
        String host = "192.168.66.36";
        int port = 22;

        // 创建JSch对象
        JSch jsch = new JSch();
        Session session = null;
        ChannelSftp ftp = null;

        try {
            // 根据主机账号、ip、端口
            session = jsch.getSession(username, host, port);
            // 设置主机密码
            session.setPassword(password);

            // 去掉首次连接确认
            session.setConfig("StrictHostKeyChecking", "on");

            // 超时连接时间为3秒
            session.setTimeout(3000);

            // 进行连接
            session.connect();

            // 打开sftp通道
            ftp = (ChannelSftp) session.openChannel("sftp");

            // 建立sftp通道的连接
            ftp.connect();

            // 设置编码
            ftp.setFilenameEncoding(CharsetUtil.CHARSET_UTF_8);

            /**
             * 说明:
             * 1、当前文件上传信息没有任何反馈,如果没有异常则代表成功
             * 2、如果需要判断是否读取成功的进度,可参考https://blog.csdn.net/coding99/article/details/52416373?locationNum=13&fps=1
             * 3、将src文件上传到dst路径中
             */

            // 上传文件
            ftp.put("d:\\123\\php-7.4.28.tar.gz", "/root/php.tar.gz");

            log.info("文件上传成功");

        } catch (SftpException | JSchException e) {
            log.warn(e.getMessage());
        } finally {
            // 关闭ftp
            if (ftp != null && ftp.isConnected()) {
                // ftp.disconnect();
                ftp.quit();
            }

            // 关闭session流
            if (session != null && session.isConnected()) {
                session.disconnect();
            }
        }
    }
}

2.3 文件下载

  1. # 说明
  2. 当前Demo适用场景:对指定虚拟机下载一个文件
package com.ix.utils;

import com.jcraft.jsch.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Properties;

public class JSchDownload {

    private static final Logger log = LoggerFactory.getLogger(JSchDownload.class);

    public static void main(String[] args) {

        String username = "root";
        String password = "123456";
        String host = "192.168.66.36";
        int port = 22;

        // 创建JSch对象
        JSch jsch = new JSch();
        Session session = null;
        ChannelSftp ftp = null;

        try {
            // 根据主机账号、ip、端口
            session = jsch.getSession(username, host, port);
            // 设置主机密码
            session.setPassword(password);

            // 去掉首次连接确认
            session.setConfig("StrictHostKeyChecking", "on");

            // 超时连接时间为3秒
            session.setTimeout(3000);

            // 进行连接
            session.connect();

            // 打开sftp通道
            ftp = (ChannelSftp) session.openChannel("sftp");

            // 建立sftp通道的连接
            ftp.connect();

            // 设置编码
            ftp.setFilenameEncoding(CharsetUtil.CHARSET_UTF_8);

           /**
             * 说明:
             * 1、当前上读取文件信息没有任何反馈,如果没有异常则代表成功
             * 2、如果需要判断是否读取成功的进度,可参考https://blog.csdn.net/coding99/article/details/52416373?locationNum=13&fps=1
             * 3、将src文件下载到dst路径中
             */
           
            // 下载文件
            ftp.get("/root/php.tar.gz", "d:/123");
			
			log.info("文件下载成功");
			
        } catch (SftpException | JSchException e) {
            log.warn(e.getMessage());
        } finally {
            // 关闭ftp
            if (ftp != null && ftp.isConnected()) {
                // ftp.disconnect();
                ftp.quit();
            }

            // 关闭session流
            if (session != null && session.isConnected()) {
                session.disconnect();
            }
        }
    }
}

2.4 执行shell命令

package com.ix.utils;

import com.jcraft.jsch.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.InputStream;
import java.util.Properties;

public class JSchExecShell {

    private static final Logger log = LoggerFactory.getLogger(JSchExecShell.class);

    public static void main(String[] args) {

        String username = "root";
        String password = "123456";
        String host = "192.168.66.36";
        int port = 22;

        // 创建
        JSch jSch = new JSch();
        Session session = null;
        ChannelExec exec = null;

        // 存放执行命令结果
        StringBuffer result = new StringBuffer();
        int exitStatus = 0;
        String command = "ls -l";

        // 创建session
        try {
            session = jSch.getSession(username, host, port);
            session.setPassword(password);
            session.setConfig("StrictHostKeyChecking", "no");
            // 设置连接超时时间
            session.setTimeout(3000);
            session.connect();

            exec = (ChannelExec) session.openChannel("exec");
            exec.setCommand(command);

            exec.setInputStream(null);
            // 错误信息输出流,用于输出错误信息,当exitstatus<0的时候
            exec.setErrStream(System.err);

            // 执行命令,等待执行结果
            exec.connect();

            // 获取命令行结果
            InputStream in = exec.getInputStream();

            // 通过channel获取信息的方式,采用官网的demo代码
            byte[] tmp = new byte[1024];
            while (true) {
                while (in.available() > 0) {
                    int i = in.read(tmp, 0, 1024);
                    if (i < 0) {
                        break;
                    }
                    result.append(new String(tmp, 0, i));
                }

                // 从channel 获取全部信息之后,channel会自动关闭
                if (exec.isClosed()) {
                    if (in.available() > 0) {
                        continue;
                    }
                    exitStatus = exec.getExitStatus();
                    break;
                }
                try {
                    Thread.sleep(1000);
                } catch (Exception e) {

                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 关闭 ftp
            if (exec != null && exec.isConnected()) {
                exec.disconnect();
            }
            // 关闭 session
            if (session != null && session.isConnected()) {
                session.disconnect();
            }
        }
        log.info("获取执行命令的结果是:" + FileUtil.getLineSeparator() + result);
        log.info("退出码为:" + FileUtil.getLineSeparator() + exitStatus);
    }
}

2.5 交互执行shell命令

  1. # 说明
  2. 当前Shell交互有弊端,执行完毕之后没有进行资源释放
package com.ix.utils;

import com.jcraft.jsch.*;
import java.util.Properties;

public class JSchInteractionShell{
    public static void main(String[] arg){

        String username = "root";
        String password = "123456";
        String host = "192.168.66.36";
        int port = 22;

        // 创建
        JSch jSch = new JSch();
        Session session = null;
        ChannelShell shell = null;

        try {
            session = jSch.getSession(username, host, port);
            session.setPassword(password);
            session.setConfig("StrictHostKeyChecking", "no");
            session.setTimeout(3000);
            session.connect();

            shell = (ChannelShell) session.openChannel("shell");
            shell.setInputStream(System.in);
            shell.setOutputStream(System.out);
            shell.connect();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

2.6 通过公钥免密登录

package com.ix.utils;

import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Properties;

public class JSchIdentity {

    private static final Logger log = LoggerFactory.getLogger(JSchIdentity.class);

    public static void main(String[] args) {

        String username = "root";
        String host = "192.168.66.36";
        int port = 22;

        // 参加jsch对象
        JSch jSch = new JSch();
        Session session = null;
        boolean result = false;

        try {
            jSch.setKnownHosts("~/.ssh/known_hosts");   // 信任的主机
            jSch.addIdentity("~/.ssh/id_rsa");          // 私钥文件

            session = jSch.getSession(username, host, port);

            // 去掉首次连接确认
            session.setConfig("StrictHostKeyChecking", "no");

            // 超时连接
            session.setTimeout(3000);

            // 进行连接
            session.connect();

            // 获取连接结果
            result = session.isConnected();

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (session != null && session.isConnected()) {
                session.disconnect();
            }
        }

        if (result) {
            log.info("【SSH连接】连接成功");
        } else {
            log.info("【SSH连接】连接失败");
        }
    }
}

第 3 章 提取工具类

3.1 单次使用工具类

JschUtil.java

package com.ix.utils;

import com.jcraft.jsch.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.nio.charset.Charset;
import java.util.Properties;

public class JschUtil {

    private static final Logger logger = LoggerFactory.getLogger(JschUtil.class);

    private JSch jSch;

    // session对象
    private Session session;

    // JAVA与主机的连接通道
    private Channel channel;

    // sftp通道
    ChannelSftp ftp;

    // 主机ip
    private String host;

    // 主机端口号
    private int port;

    // 主机账号
    private String username;

    // 主机密码
    private String password;

    public JschUtil(String host, int port, String username, String password) {
        this.host = host;
        this.port = port;
        this.username = username;
        this.password = password;
    }

    public JschUtil() {
    }

    /**
     * 检测是否可以和主机通信
     * @return
     */
    public boolean connect() {

        jSch = new JSch();

        boolean result = false;

        try {

            // 根据主机账号、ip、端口获取一个Session对象
            session = jSch.getSession(username, host, port);

            // 存放主机密码
            session.setPassword(password);

            // 首次连接,去掉公钥确认
            Properties config = new Properties();
            config.put("StrictHostKeyChecking", "no");
            session.setConfig(config);

            // 超时连接时间为3秒
            session.setTimeout(3000);

            // 进行连接
            session.connect();

            // 获取连接结果
            result = session.isConnected();

            if (!result) {
                logger.error("【连接】获取连接失败");
            }

        } catch (JSchException e) {
            logger.warn(e.getMessage());
        } finally {
            close();
        }
        return result;
    }

    /**
     * 关闭连接
     */
    public void close() {

        if (channel != null && channel.isConnected()) {
            channel.disconnect();
        }

        if (session != null && session.isConnected()) {
            session.disconnect();
        }

        if (ftp != null && ftp.isConnected()) {
            ftp.quit();
        }

    }

    /**
     * 执行shell命令
     * @param command
     * @return
     */
    public String execCommand(String command) {

        jSch = new JSch();

        // 存放执行命令结果
        StringBuffer result = new StringBuffer();
        int exitStatus = 0;

        try {

            // 根据主机账号、ip、端口获取一个Session对象
            session = jSch.getSession(username, host, port);

            // 存放主机密码
            session.setPassword(password);

            // 去掉首次连接确认
            Properties config = new Properties();
            config.put("StrictHostKeyChecking", "no");
            session.setConfig(config);

            // 超时连接时间为3秒
            session.setTimeout(3000);

            // 进行连接
            session.connect();

            channel = session.openChannel("exec");
            ((ChannelExec) channel).setCommand(command);

            channel.setInputStream(null);
            // 错误信息输出流,用于输出错误的信息,当exitstatus<0的时候
            ((ChannelExec)channel).setErrStream(System.err);

            // 执行命令,等待执行结果
            channel.connect();

            // 获取命令执行结果
            InputStream in = channel.getInputStream();

            /**
             * 通过channel获取信息的方式,采用官方Demo代码
             */
            byte[] tmp=new byte[1024];
            while(true){
                while(in.available() > 0){
                    int i = in.read(tmp, 0, 1024);
                    if (i < 0) {
                        break;
                    }
                    result.append(new String(tmp, 0, i));
                }
                // 从channel获取全部信息之后,channel会自动关闭
                if(channel.isClosed()){
                    if (in.available() > 0) {
                        continue;
                    }
                    exitStatus = channel.getExitStatus();
                    break;
                }
                try{Thread.sleep(1000);}catch(Exception ee){}
            }


        } catch (IOException e) {
            logger.error(e.getMessage());
        } catch (JSchException e) {
            logger.error(e.getMessage());
        } finally {
            close();
        }

        logger.info("退出码为:"+exitStatus);
        return result.toString();
    }

    /**
     * 文件上传至主机
     * @param directory 当前文件路径
     * @param uploadFile 上传至主机的路径
     */
    public void upload(String directory, String uploadFile) {
        // 创建JSch对象
        jSch = new JSch();

        try {
            // 根据主机账号、ip、端口获取一个Session对象
            session = jSch.getSession(username, host, port);

            // 存放主机密码
            session.setPassword(password);

            // 去掉首次连接确认
            Properties config = new Properties();
            config.put("StrictHostKeyChecking", "no");
            session.setConfig(config);

            // 超时连接时间为3秒
            session.setTimeout(3000);

            // 进行连接
            session.connect();

            // 打开SFTP通道
            ftp = (ChannelSftp)session.openChannel("sftp");

            // 建立STFP连接
            ftp.connect();

            // 设置编码格式
            ftp.setFilenameEncoding("UTF-8");

            /**
             * 说明:
             * 1、当前文件上传信息没有任何反馈,如果没有异常则代表成功
             * 2、如果需要判断是否读取成功的进度,可参考https://blog.csdn.net/coding99/article/details/52416373?locationNum=13&fps=1
             * 3、将src文件上传到dst路径中
             */
            ftp.put(directory, uploadFile);
            logger.info("文件上传成功");
        } catch (JSchException | SftpException e) {
            logger.warn(e.getMessage());
        } finally {
            close();
        }
    }

    /**
     * 将主机文件下载至本地
     * @param directory  下载到本地的位置
     * @param downloadFile  下载文件在虚拟机的位置
     */
    public void download(String directory, String downloadFile) {

        try {
            jSch = new JSch();

            // 根据主机账号、ip、端口获取一个Session对象
            session = jSch.getSession(username, host, port);

            // 存放主机密码
            session.setPassword(password);

            // 去掉首次连接确认
            Properties config = new Properties();
            config.put("StrictHostKeyChecking", "no");
            session.setConfig(config);

            // 超时连接时间为3秒
            session.setTimeout(3000);

            // 进行连接
            session.connect();

            // 打开SFTP通道
            ftp = (ChannelSftp)session.openChannel("sftp");

            // 建立SFTP通道的连接
            ftp.connect();

            // 设置编码格式
            ftp.setFilenameEncoding("UTF-8");

            /**
             * 说明:
             * 1、当前上读取文件信息没有任何反馈,如果没有异常则代表成功
             * 2、如果需要判断是否读取成功的进度,可参考https://blog.csdn.net/coding99/article/details/52416373?locationNum=13&fps=1
             * 3、将src文件下载到dst路径中
             */
            ftp.get(directory, downloadFile);

            logger.info("文件下载成功");
        } catch (JSchException | SftpException e) {
            logger.warn(e.getMessage());
        }  finally {
            close();
        }
    }
}

RunJsch.java

package com.ix.utils;

public class RunJsch {
    public static void main(String[] args) {

        JschUtil jSchUtil = new JschUtil("192.168.66.36",22,"root","123456");
        boolean connect = jSchUtil.connect();
        System.out.println("ssh连接检测:" + connect);

        String ls = jSchUtil.execCommand("ls");
        System.out.println("执行ls命令的结果:" + ls);

		// 文件上传
        jSchUtil.upload("D:/2.txt", "/home/1.txt");

        // 文件下载
        jSchUtil.download("/home/1.txt", "D:/node1.txt");

    }
}

 

 

参考:https://blog.csdn.net/weixin_43898952/article/details/119967566