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;
}
}