没长正的技术专栏 勤动手、多思考

大数据工具JSCH

2020-01-02

阅读:


SSH工具

场景

核心:通过ssh 连接 Linux服务器,实现运维相关工作,(执行脚本、上传、下载等)

Jsch

JSch 是SSH2的一个纯Java实现。它允许你连接到一个sshd 服务器,使用端口转发,X11转发,文件传输等等。你可以将它的功能集成到你自己的 程序中。同时该项目也提供一个J2ME版本用来在手机上直连SSHD服务器。


环境配置类

@Configuration
@Data
public class JSchConnectConfig implements Serializable {

    /**
     * 是否密码登录
     */
    @Value("${jsch.passwordLogin:false}")
    private Boolean passwordLogin;

    /**
     * 主机
     */
    @Value("${jsch.host:192.168.83.165}")
    private String host;

    /**
     * 端口
     */
    @Value("${jsch.port:22}")
    private Integer port;

    /**
     * 用户名
     */
    @Value("${jsch.userName:omm}")
    private String userName;

    /**
     * 密码
     */
    @Value("${jsch.password:password}")
    private String passWord;

    /**
     * 私钥
     */
    @Value("${jsch.privateKey:privateKey}")
    private String privateKey;

    /**
     * 30分钟
     * 超时时间  ms  (1000 * 60 * 30)
     */
    @Value("${jsch.timeout:1800000}")
    private Integer timeout;

    @Value("${spring.profiles.active:dev}")
    private String active;
}

JSchUtil 工具类


import com.jcraft.jsch.*;
import com.xinchao.dataschedule.web.config.JSchConnectConfig;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.io.*;
import java.util.List;
import java.util.Properties;

@Slf4j
public class JSchUtil {

    /**
     * exec
     */
    public static final String EXEC = "exec";
    public static final String SFTP = "sftp";
    public static final String SHELL = "shell";

    /**
     * 通道Exec
     */
    private ChannelExec channelExec;

    /**
     * 通道Sftp
     */
    private ChannelSftp channelSftp;

    /**
     * 通道Shell
     */
    private ChannelShell channelShell;

    /**
     * session
     */
    private Session session;

    /**
     * 规避多线程并发
     */
    private static ThreadLocal<JSchUtil> execLocal = new ThreadLocal<>();

    /**
     * 获取execchannel
     *
     * @param connectConfig 连接配置
     * @return
     * @throws Exception
     * @throws JSchException
     */
    private void init(JSchConnectConfig connectConfig) throws Exception {

        log.info("连接配置信息:{}", connectConfig);
        String host = connectConfig.getHost();
        int port = connectConfig.getPort();
        String userName = connectConfig.getUserName();

        //创建JSch对象
        JSch jsch = new JSch();

        if (!connectConfig.getPasswordLogin().booleanValue()) {
            //添加私钥(信任登录方式)
            if (StringUtils.isBlank(connectConfig.getPrivateKey())) {
                log.error("JSch private key 为空");
                return;
            }
            String filePath = "";
            if ("dev".equals(connectConfig.getActive())) {
                filePath = this.getClass().getResource("/").getPath() + PATH_DELIMITER + connectConfig.getPrivateKey();
            } else {
                filePath = PATH_DELIMITER + connectConfig.getPrivateKey();
            }
            jsch.addIdentity(filePath);
        }

        session = jsch.getSession(userName, host, port);
        if (log.isInfoEnabled()) {
            log.info(" JSCH Session created,execHost = {}, execUserName={}", host, userName);
        }

        //设置密码
        if (connectConfig.getPasswordLogin().booleanValue()) {
            if (StringUtils.isBlank(connectConfig.getPassWord())) {
                log.error("JSch password 为空");
                return;
            }
            session.setPassword(connectConfig.getPassWord());
        }

        Properties config = new Properties();
        config.put("StrictHostKeyChecking", "no");
        session.setConfig(config);
        //设置超时
        session.setTimeout(connectConfig.getTimeout());
        session.setServerAliveInterval(5000);
        //建立连接
        session.connect();
        if (log.isInfoEnabled()) {
            log.info("JSCH Session connected.execHost = {}, execUserName={}", host, userName);
        }

        if (log.isInfoEnabled()) {
            log.info("Connected successfully to execHost = {}, execUserName={}", host, userName);
        }
    }

    /**
     * 是否已连接
     *
     * @return 是否连接成功
     */
    private boolean isConnected() {
        return null != channelExec && channelExec.isConnected();
    }

    /**
     * 获取本地线程存储的exec客户端
     *
     * @return
     * @throws Exception 异常
     */
    public static JSchUtil getSftpUtil(JSchConnectConfig connectConfig) throws Exception {
        JSchUtil execUtil = execLocal.get();
        if (null == execUtil || !execUtil.isConnected()) {
            execLocal.set(new JSchUtil(connectConfig));
        }
        return execLocal.get();
    }

    /**
     * 释放本地线程存储的exec客户端
     */
    public static void release() {
        if (null != execLocal.get()) {
            execLocal.get().closeChannel();
            execLocal.remove();
        }
    }

    /**
     * 构造函数
     * <p>
     * 非线程安全,故权限为私有
     * </p>
     *
     * @throws Exception 异常
     */
    private JSchUtil(JSchConnectConfig connectConfig) throws Exception {
        super();
        init(connectConfig);
    }

    /**
     * 关闭通道
     *
     * @throws Exception 异常
     */
    public void closeChannel() {
        if (null != channelExec) {
            try {
                channelExec.disconnect();
            } catch (Exception e) {
                log.error("关闭EXEC通道发生异常:", e);
            }
        }
        if (null != channelSftp) {
            try {
                channelSftp.disconnect();
            } catch (Exception e) {
                log.error("关闭SFTP通道发生异常:", e);
            }
        }
        if (null != channelShell) {
            try {
                channelShell.disconnect();
            } catch (Exception e) {
                log.error("关闭SHELL通道发生异常:", e);
            }
        }
        if (null != session) {
            try {
                session.disconnect();
            } catch (Exception e) {
                log.error("EXEC关闭 session异常:", e);
            }
        }
    }

    /**
     * returnCode :0 :正常  其余表示异常  (不能活返回结果)
     *
     * @param commands 命令
     * @return 结果
     * @throws IOException   IO异常
     * @throws JSchException JSch异常
     */
    public int execShell(List<String> commands) throws JSchException, IOException {

        log.info("exec shell 执行的脚本为:{}", commands);
        int returnCode = 0;
        channelShell = (ChannelShell) session.openChannel(SHELL);

        // 输入重定向
        PipedOutputStream pipe = new PipedOutputStream();
        PipedInputStream in = new PipedInputStream(pipe);
        PrintWriter pw = new PrintWriter(pipe);

        channelShell.setInputStream(in);

        // 输出重定向
        PrintStream out = System.out;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        PrintStream ps = new PrintStream(baos);
        System.setOut(ps);
        //channelShell.setOutputStream(out); 不设置输出,不然重定向异常

        for (String cmd : commands) {
            pw.println(cmd);
        }
        pw.println("exit");
        pw.flush();
        channelShell.connect();

        // 等待关闭
        while (!channelShell.isClosed()) {
        }
        // 重定向回来
        System.out.flush();
        System.setOut(out);
        returnCode = channelShell.getExitStatus();
        log.info("返回的结果为:{}", returnCode);
        log.info(baos.toString());
        channelShell.disconnect();
        session.disconnect();

        return returnCode;
    }

    /**
     * returnCode :0 :正常  其余表示异常  (不能活返回结果)
     *
     * @param command 命令
     * @return 结果
     * @throws IOException   IO异常
     * @throws JSchException JSch异常
     */
    public int execCommand(String command) throws IOException, JSchException {

        log.info("exec 执行的脚本为:{}", command);
        int returnCode = -1;
        if (channelExec == null || channelExec.isClosed()) {
            channelExec = (ChannelExec) session.openChannel(EXEC);
        }
        channelExec.setCommand(command);
        channelExec.setInputStream(null);
        channelExec.setErrStream(System.err);
        InputStream in = channelExec.getInputStream();
        channelExec.connect(2000);

        byte[] tmp = new byte[1024];
        while (true) {
            while (in.available() > 0) {
                int i = in.read(tmp, 0, 1024);
                if (i < 0) {
                    break;
                }
                log.info("exec 返回的内容为:{}", new String(tmp, 0, i));
            }
            if (channelExec.isClosed()) {
                if (in.available() > 0) {
                    continue;
                }
                returnCode = channelExec.getExitStatus();
                log.info("exit-status:{}", returnCode);
                break;
            }
        }
        return returnCode;
    }

    /**
     * 从 服务器 获取 数据
     *
     * @param filePath 文件路径
     */
    public InputStream getLog(String filePath) {

        try {
            log.info("日志文件路径为:{}", filePath);
            channelSftp = (ChannelSftp) session.openChannel(SFTP);
            channelSftp.connect();
            return channelSftp.get(filePath);

        } catch (SftpException | JSchException e) {
            log.error("下载文件异常:{}", e);
        }
        return null;
    }

}

相关

参考资料

Jsch

Jsch示例

Jsch环境问题

资源重定向问题

Jsch工具类

Jsch公钥、工具


Similar Posts

欢迎拍砖,多多交流,转载请注明出处:[没长正的技术专栏](http://blog.meizhangzheng.com) 如涉及侵权问题,请发送邮件到xsj34567@163.com,如情况属实本人将会尽快删除。


上一篇 大数据

Comments