JSch封装简单工具类

678 阅读2分钟

背景

项目开发中需要通过Java服务远程登录机器,执行指定路径下的shell脚本文件。经过简单的技术调研后,选择了JSch。

JSch介绍

官网:www.jcraft.com/jsch/

JSch(Java Secure Channel),是SSH2的一个纯Java实现。 JSch允许用户连接到一个sshd服务器,并使用端口转发,X11转发,文件传输等等功能。

使用

项目是用Maven构建的,故需要引入pom依赖

1、导入Maven依赖

<dependency>
    <groupId>com.jcraft</groupId>
    <artifactId>jsch</artifactId>
    <version>${jsch.version}</version>
</dependency>

2、封装工具类

注:我在项目中的需求仅仅是ssh远程登录并执行shell脚本,没有考虑其他复杂场景,更多内容可以参考官方文档。

public class SSHUtil implements Closeable {
    private Session session;
    private Channel channel;
    private ChannelExec channelExec;

    private final String host;
    private final Integer port;
    private final String username;
    private final String password;

    public SSHUtil(String host, Integer port, String username, String password) {
        this.host = host;
        this.port = port;
        this.username = username;
        this.password = password;
    }

    /**
     * 初始化
     * @throws JSchException
     */
    private void init() throws JSchException {
        // 创建JSch对象
        JSch jSch = new JSch();
        // 获取一个Session对象
        session = jSch.getSession(username, host, port);
        // 设置密码
        session.setPassword(password);
        
        Properties config = new Properties();
        config.put("StrictHostKeyChecking", "no");
        session.setConfig(config);
        // 进行连接,设置连接超时时间
        session.connect(6000);
        log.info("session connected!");
        // 打开执行shell指令的通道
        channel = session.openChannel("exec");
        channelExec = (ChannelExec) channel;
    }

    /**
     * 执行命令
     * @param command
     * @throws Exception
     */
    public String execCmd(String command) throws Exception {
        init();
        if (session == null || channel == null || channelExec == null) {
            throw new Exception("please invoke init(...) first!");
        }
        log.info("执行命令:{}", command);
        channelExec.setCommand(command);
        channel.setInputStream(null);
        // 错误信息输出流,用于输出错误的信息
        channelExec.setErrStream(System.err);
        // 执行命令
        channel.connect();
        // 获取命令执行结果
        StringBuilder sb = new StringBuilder(16);
        try (InputStream in = channelExec.getInputStream();
             InputStreamReader isr = new InputStreamReader(in, StandardCharsets.UTF_8);
             BufferedReader reader = new BufferedReader(isr)) {
            String buffer;
            while ((buffer = reader.readLine()) != null) {
                sb.append("\n").append(buffer);
            }
        } catch (Exception e) {
            log.info("执行失败:{}", e.toString());
        }
        log.info("执行成功:{}", sb);
        return sb.toString();
    }

    @Override
    public void close() {
        if (channelExec != null && channelExec.isConnected()) {
            channelExec.disconnect();
        }
        if (channel != null && channel.isConnected()) {
            channel.disconnect();
        }
        if (session != null && session.isConnected()) {
            session.disconnect();
        }
    }

    public static void main(String[] args) {
        try(SSHUtil sshUtil = new SSHUtil("11.11.11.11",22,"xxx","xxxxxx")) {
            String result = sshUtil.execCmd("sh /data/test.sh");
        } catch (Exception e) {
            log.error("执行失败",e);
        }
    }

补充:在测试过程中,代码执行报错connection refused,经排查发现运维将ssh默认的端口号22改成了其他数字,使用时发现类似情况可以借鉴。