JSch 是SSH2的一个纯Java实现。它允许通过代码的方式连接到一个sshd 服务器,使用端口转发,X11转发,文件传输等等。可以将它的功能集成到自己写的程序中。实现一个java工具类。

SSH由客户端和服务端的软件两部分组成,在客户端可以使用的软件有SecureCRT、putty、Xshell等,而在服务器端运行的是一个sshd的服务,通过使用SSH,可以把所有传输的数据进行加密,而且也能够防止dns和IP欺骗,此外,SSH传输的数据是经过压缩的,可以加快传输速度。服务器端的配置文件路径: /etc/ssh/sshd_config

spring-boot引用:

dependencies {

    compile group: 'com.jcraft', name: 'jsch', version: '0.1.55'

}

代码里的命令调用:

try {
            String result = clusterCommondService.execOnly(
                    "xxxxxxxxx " + name + " xxxx" + "\n");
        } catch (Exception e) {
            log.error("Error when init :", e);
            return null;
        }

 

java代码实现

@Slf4j
@Service
public class ClusterCommondService {

    @Value("${cluster.pemPrivateKey}")
    protected String pemPrivateKey;
    @Value("${cluster.port}")
    protected int port;
    @Value("${cluster.user}")
    protected String user;
//写在配置文件里的配置值
    @Autowired
    private ClusterIpUtil clusterIpUtil;
//连接的IP
    private Session session = null;
    private boolean bConnect = false;

//默认第一次是false,先建立连接

protected String ip;

    private void initConnection() throws JSchException {
        ip = clusterIpUtil.getClusterIp();
        String begin = "-----BEGIN RSA PRIVATE KEY-----";
        String end = "-----END RSA PRIVATE KEY-----";
        String a = pemPrivateKey.replace(" ", "\n");
        String privateKey = begin + "\n" + a + "\n" + end;
        JSch jsch = new JSch();
        jsch.addIdentity(user, privateKey.getBytes(), null, null);
        session = jsch.getSession(user, ip, port);
        session.setConfig("PreferredAuthentications", "publickey");
        session.setConfig("StrictHostKeyChecking", "no");
        session.setConfig("userauth.gssapi-with-mic", "no");
        session.setConfig("UserKnownHostsFile", "/dev/null");
        session.setServerAliveCountMax(3);
//连接三次,连不上就不连接了
session.connect();
        bConnect = true;
    }
//设置连接信息,应该是代替了客户端的/etc/ssh/ssh_config

    public String exec(String cmd) throws JSchException, IOException {
        if (!bConnect) {
            initConnection();
        }
        ChannelExec channelExec = (ChannelExec) session.openChannel("exec");
//使用exec方式建立连接(补充,jsch跑命令分两种方式,一种是exec,一种是sftp;从远端拿文件或者向远端传文件的话用sftp,跑命令然后拿返回值的话用exec)
        channelExec.setCommand(cmd);
//把命令放进连接里
        channelExec.setInputStream(null);
        channelExec.setErrStream(System.err);
//设置输入流和错误信息流,清空输入流
        InputStream in = channelExec.getInputStream();
//建一个in是输入流
        channelExec.connect();
//建立连接,和远端建立连接之后上面的命令就自动执行了(此时的in里面就有返回值了)
        int res = -1;
        StringBuilder buf = new StringBuilder(1024);
        byte[] tmp = new byte[1024];
        while (true) {
            while (in.available() > 0) {
                int i = in.read(tmp, 0, 1024);
                if (i < 0) break;
//这如果读不到了,就说明读完了命令,就可以退出去了
                buf.append(new String(tmp, 0, i));
            }
            if (channelExec.isClosed()) {
//判断连接关闭,这个是在把信息读出来之后判断的
                res = channelExec.getExitStatus();
//这个是状态码,正常是0,如果运行命令出问题就不是0了;不同的数值代表不同的错误
                if (res != 0)
                    log.error(format("Exit-status: %d", res));
                break;
            }
        }
        if (res != 0){
            log.error(buf.toString());
            channelExec.disconnect();
//操作完后channel连接关闭
            return null;
        }
        channelExec.disconnect();
//操作完后channel连接关闭
return buf.toString();
    }

    private String download(String downloadFile) throws JSchException, SftpException, IOException {
        if (!bConnect)
            initConnection();
        ChannelSftp channelSftp = (ChannelSftp) session.openChannel("sftp");
        channelSftp.connect();

        StringBuilder result = new StringBuilder();
        String temp = "";
        InputStream is = channelSftp.get(downloadFile);
        byte[] buff = new byte[1024 * 2];
        int read;
        if (is != null) {
            log.debug("Start to read input stream");
            do {
                read = is.read(buff, 0, buff.length);
                if (read > 0) {
                    temp = new String(buff);
                    result.append(temp);
                }
            } while (read >= 0);
            log.debug("input stream read done.");
        }
        channelSftp.disconnect();
        return result.toString();
    }

    public synchronized String execOnly(String cmd) throws IOException, JSchException {
        String result = exec(cmd);
        close();
//在close()里边把bconnect置为false,关闭此次连接。(每调用一次后,就关闭连接)
        return result;
    }

    public String execAndDownload(String cmd) throws IOException, JSchException, SftpException {
        String fileName = exec(cmd);
        String fileData = download(fileName.replace("\n", ""));
        close();
//ession连接关闭
        return fileData;
    }

    private void close() {
        session.disconnect();
        bConnect = false;
    }
}

session是跟远程主机建立一个连接,JSch 在这个连接上建立一个通道专门执行命令,setCommand就是一个字节数组,存储我要执行的命令,然后打开一个流,通过这个流将我要执行的命令送到远程主机上执行,上图代码中的in里面是返回过来的数据。