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里面是返回过来的数据。