mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-04-19 03:01:48 +08:00
fix code
This commit is contained in:
parent
8a0e477f69
commit
ed0b9a16ef
@ -16,6 +16,7 @@ import ch.ethz.ssh2.Connection;
|
||||
import ch.ethz.ssh2.StreamGobbler;
|
||||
import org.dromara.hutool.core.io.IORuntimeException;
|
||||
import org.dromara.hutool.core.io.IoUtil;
|
||||
import org.dromara.hutool.core.util.CharsetUtil;
|
||||
import org.dromara.hutool.extra.ssh.Connector;
|
||||
import org.dromara.hutool.extra.ssh.Session;
|
||||
|
||||
@ -65,7 +66,7 @@ public class GanymedSession implements Session {
|
||||
/**
|
||||
* 执行Shell命令(使用EXEC方式)
|
||||
* <p>
|
||||
* 此方法单次发送一个命令到服务端,不读取环境变量,执行结束后自动关闭Session,不会产生阻塞。
|
||||
* 此方法单次发送一个命令到服务端,不读取环境变量,不会产生阻塞。
|
||||
* </p>
|
||||
*
|
||||
* @param cmd 命令
|
||||
@ -73,24 +74,31 @@ public class GanymedSession implements Session {
|
||||
* @param errStream 错误信息输出到的位置
|
||||
* @return 执行返回结果
|
||||
*/
|
||||
public String exec(final String cmd, final Charset charset, final OutputStream errStream) {
|
||||
final String result;
|
||||
public String exec(final String cmd, Charset charset, final OutputStream errStream) {
|
||||
if (null == charset) {
|
||||
charset = CharsetUtil.UTF_8;
|
||||
}
|
||||
|
||||
// 发送命令
|
||||
try {
|
||||
this.raw.execCommand(cmd, charset.name());
|
||||
result = IoUtil.read(new StreamGobbler(this.raw.getStdout()), charset);
|
||||
|
||||
// 错误输出
|
||||
IoUtil.copy(new StreamGobbler(this.raw.getStderr()), errStream);
|
||||
} catch (final IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
}
|
||||
return result;
|
||||
|
||||
// 错误输出
|
||||
if(null != errStream){
|
||||
IoUtil.copy(new StreamGobbler(this.raw.getStderr()), errStream);
|
||||
}
|
||||
|
||||
// 结果输出
|
||||
return IoUtil.read(new StreamGobbler(this.raw.getStdout()), charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行Shell命令
|
||||
* <p>
|
||||
* 此方法单次发送一个命令到服务端,自动读取环境变量,执行结束后自动关闭Session,可能产生阻塞。
|
||||
* 此方法单次发送一个命令到服务端,自动读取环境变量,可能产生阻塞。
|
||||
* </p>
|
||||
*
|
||||
* @param cmd 命令
|
||||
@ -98,21 +106,27 @@ public class GanymedSession implements Session {
|
||||
* @param errStream 错误信息输出到的位置
|
||||
* @return 执行返回结果
|
||||
*/
|
||||
public String execByShell(final String cmd, final Charset charset, final OutputStream errStream) {
|
||||
final String result;
|
||||
public String execByShell(final String cmd, Charset charset, final OutputStream errStream) {
|
||||
if (null == charset) {
|
||||
charset = CharsetUtil.UTF_8;
|
||||
}
|
||||
|
||||
try {
|
||||
this.raw.requestDumbPTY();
|
||||
IoUtil.write(this.raw.getStdin(), charset, true, cmd);
|
||||
|
||||
result = IoUtil.read(new StreamGobbler(this.raw.getStdout()), charset);
|
||||
if(null != errStream){
|
||||
// 错误输出
|
||||
IoUtil.copy(new StreamGobbler(this.raw.getStderr()), errStream);
|
||||
}
|
||||
} catch (final IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
}
|
||||
return result;
|
||||
|
||||
// 发送命令
|
||||
IoUtil.write(this.raw.getStdin(), charset, true, cmd);
|
||||
|
||||
// 错误输出
|
||||
if (null != errStream) {
|
||||
IoUtil.copy(new StreamGobbler(this.raw.getStderr()), errStream);
|
||||
}
|
||||
|
||||
// 结果输出
|
||||
return IoUtil.read(new StreamGobbler(this.raw.getStdout()), charset);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -205,6 +205,7 @@ public class JschSession implements Session {
|
||||
final ChannelExec channel = (ChannelExec) createChannel(ChannelType.EXEC);
|
||||
channel.setCommand(ByteUtil.toBytes(cmd, charset));
|
||||
channel.setInputStream(null);
|
||||
|
||||
channel.setErrStream(errStream);
|
||||
InputStream in = null;
|
||||
try {
|
||||
@ -234,7 +235,7 @@ public class JschSession implements Session {
|
||||
* @return {@link ChannelExec}
|
||||
* @since 5.2.5
|
||||
*/
|
||||
public static String execByShell(final String cmd, final Charset charset) {
|
||||
public String execByShell(final String cmd, final Charset charset) {
|
||||
final ChannelShell shell = openShell();
|
||||
// 开始连接
|
||||
shell.setPty(true);
|
||||
|
@ -1,131 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023 looly(loolly@aliyun.com)
|
||||
* Hutool is licensed under Mulan PSL v2.
|
||||
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
* You may obtain a copy of Mulan PSL v2 at:
|
||||
* https://license.coscl.org.cn/MulanPSL2
|
||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
* See the Mulan PSL v2 for more details.
|
||||
*/
|
||||
|
||||
package org.dromara.hutool.extra.ssh.engine.jsch;
|
||||
|
||||
import org.dromara.hutool.core.cache.SimpleCache;
|
||||
import org.dromara.hutool.core.text.StrUtil;
|
||||
import com.jcraft.jsch.Session;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
/**
|
||||
* Jsch会话池
|
||||
*
|
||||
* @author looly
|
||||
*/
|
||||
public enum JschSessionPool {
|
||||
INSTANCE;
|
||||
|
||||
/**
|
||||
* SSH会话池,key:host,value:Session对象
|
||||
*/
|
||||
private final SimpleCache<String, Session> cache = new SimpleCache<>(new HashMap<>());
|
||||
|
||||
/**
|
||||
* 获取Session,不存在返回null
|
||||
*
|
||||
* @param key 键
|
||||
* @return Session
|
||||
*/
|
||||
public Session get(final String key) {
|
||||
return cache.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得一个SSH跳板机会话,重用已经使用的会话
|
||||
*
|
||||
* @param sshHost 跳板机主机
|
||||
* @param sshPort 跳板机端口
|
||||
* @param sshUser 跳板机用户名
|
||||
* @param sshPass 跳板机密码
|
||||
* @return SSH会话
|
||||
*/
|
||||
public Session getSession(final String sshHost, final int sshPort, final String sshUser, final String sshPass) {
|
||||
final String key = StrUtil.format("{}@{}:{}", sshUser, sshHost, sshPort);
|
||||
return this.cache.get(key, Session::isConnected, ()-> JschUtil.openSession(sshHost, sshPort, sshUser, sshPass));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得一个SSH跳板机会话,重用已经使用的会话
|
||||
*
|
||||
* @param sshHost 跳板机主机
|
||||
* @param sshPort 跳板机端口
|
||||
* @param sshUser 跳板机用户名
|
||||
* @param prvkey 跳板机私钥路径
|
||||
* @param passphrase 跳板机私钥密码
|
||||
* @return SSH会话
|
||||
*/
|
||||
public Session getSession(final String sshHost, final int sshPort, final String sshUser, final String prvkey, final byte[] passphrase) {
|
||||
final String key = StrUtil.format("{}@{}:{}", sshUser, sshHost, sshPort);
|
||||
return this.cache.get(key, Session::isConnected, ()->JschUtil.openSession(sshHost, sshPort, sshUser, prvkey, passphrase));
|
||||
}
|
||||
|
||||
/**
|
||||
* 加入Session
|
||||
*
|
||||
* @param key 键
|
||||
* @param session Session
|
||||
*/
|
||||
public void put(final String key, final Session session) {
|
||||
this.cache.put(key, session);
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭SSH连接会话
|
||||
*
|
||||
* @param key 主机,格式为user@host:port
|
||||
*/
|
||||
public void close(final String key) {
|
||||
final Session session = get(key);
|
||||
if (session != null && session.isConnected()) {
|
||||
session.disconnect();
|
||||
}
|
||||
this.cache.remove(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除指定Session
|
||||
*
|
||||
* @param session Session会话
|
||||
* @since 4.1.15
|
||||
*/
|
||||
public void remove(final Session session) {
|
||||
if (null != session) {
|
||||
final Iterator<Entry<String, Session>> iterator = this.cache.iterator();
|
||||
Entry<String, Session> entry;
|
||||
while (iterator.hasNext()) {
|
||||
entry = iterator.next();
|
||||
if (session.equals(entry.getValue())) {
|
||||
iterator.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭所有SSH连接会话
|
||||
*/
|
||||
public void closeAll() {
|
||||
Session session;
|
||||
for (final Entry<String, Session> entry : this.cache) {
|
||||
session = entry.getValue();
|
||||
if (session != null && session.isConnected()) {
|
||||
session.disconnect();
|
||||
}
|
||||
}
|
||||
cache.clear();
|
||||
}
|
||||
}
|
@ -1,574 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023 looly(loolly@aliyun.com)
|
||||
* Hutool is licensed under Mulan PSL v2.
|
||||
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
* You may obtain a copy of Mulan PSL v2 at:
|
||||
* https://license.coscl.org.cn/MulanPSL2
|
||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
* See the Mulan PSL v2 for more details.
|
||||
*/
|
||||
|
||||
package org.dromara.hutool.extra.ssh.engine.jsch;
|
||||
|
||||
import com.jcraft.jsch.*;
|
||||
import org.dromara.hutool.core.io.IORuntimeException;
|
||||
import org.dromara.hutool.core.io.IoUtil;
|
||||
import org.dromara.hutool.core.lang.Assert;
|
||||
import org.dromara.hutool.core.net.LocalPortGenerator;
|
||||
import org.dromara.hutool.core.text.StrUtil;
|
||||
import org.dromara.hutool.core.util.ByteUtil;
|
||||
import org.dromara.hutool.core.util.CharsetUtil;
|
||||
import org.dromara.hutool.extra.ssh.Connector;
|
||||
import org.dromara.hutool.extra.ssh.SshException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
/**
|
||||
* Jsch工具类<br>
|
||||
* Jsch是Java Secure Channel的缩写。JSch是一个SSH2的纯Java实现。<br>
|
||||
* 它允许你连接到一个SSH服务器,并且可以使用端口转发,X11转发,文件传输等。<br>
|
||||
*
|
||||
* @author Looly
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public class JschUtil {
|
||||
|
||||
/**
|
||||
* 不使用SSH的值
|
||||
*/
|
||||
public final static String SSH_NONE = "none";
|
||||
|
||||
/**
|
||||
* 本地端口生成器
|
||||
*/
|
||||
private static final LocalPortGenerator portGenerater = new LocalPortGenerator(10000);
|
||||
|
||||
/**
|
||||
* 生成一个本地端口,用于远程端口映射
|
||||
*
|
||||
* @return 未被使用的本地端口
|
||||
*/
|
||||
public static int generateLocalPort() {
|
||||
return portGenerater.generate();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得一个SSH会话,重用已经使用的会话
|
||||
*
|
||||
* @param sshHost 主机
|
||||
* @param sshPort 端口
|
||||
* @param sshUser 用户名
|
||||
* @param sshPass 密码
|
||||
* @return SSH会话
|
||||
*/
|
||||
public static Session getSession(final String sshHost, final int sshPort, final String sshUser, final String sshPass) {
|
||||
return JschSessionPool.INSTANCE.getSession(sshHost, sshPort, sshUser, sshPass);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得一个SSH会话,重用已经使用的会话
|
||||
*
|
||||
* @param sshHost 主机
|
||||
* @param sshPort 端口
|
||||
* @param sshUser 用户名
|
||||
* @param privateKeyPath 私钥路径
|
||||
* @param passphrase 私钥密码
|
||||
* @return SSH会话
|
||||
*/
|
||||
public static Session getSession(final String sshHost, final int sshPort, final String sshUser, final String privateKeyPath, final byte[] passphrase) {
|
||||
return JschSessionPool.INSTANCE.getSession(sshHost, sshPort, sshUser, privateKeyPath, passphrase);
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开一个新的SSH会话
|
||||
*
|
||||
* @param sshHost 主机
|
||||
* @param sshPort 端口
|
||||
* @param sshUser 用户名
|
||||
* @param sshPass 密码
|
||||
* @return SSH会话
|
||||
*/
|
||||
public static Session openSession(final String sshHost, final int sshPort, final String sshUser, final String sshPass) {
|
||||
return openSession(sshHost, sshPort, sshUser, sshPass, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开一个新的SSH会话
|
||||
*
|
||||
* @param sshHost 主机
|
||||
* @param sshPort 端口
|
||||
* @param sshUser 用户名
|
||||
* @param sshPass 密码
|
||||
* @param timeout Socket连接超时时长,单位毫秒
|
||||
* @return SSH会话
|
||||
* @since 5.3.3
|
||||
*/
|
||||
public static Session openSession(final String sshHost, final int sshPort, final String sshUser, final String sshPass, final int timeout) {
|
||||
final Session session = createSession(sshHost, sshPort, sshUser, sshPass);
|
||||
try {
|
||||
session.setTimeout(timeout);
|
||||
session.connect(timeout);
|
||||
} catch (final JSchException e) {
|
||||
throw new SshException(e);
|
||||
}
|
||||
return session;
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开一个新的SSH会话
|
||||
*
|
||||
* @param sshHost 主机
|
||||
* @param sshPort 端口
|
||||
* @param sshUser 用户名
|
||||
* @param privateKeyPath 私钥的路径
|
||||
* @param passphrase 私钥文件的密码,可以为null
|
||||
* @return SSH会话
|
||||
*/
|
||||
public static Session openSession(final String sshHost, final int sshPort, final String sshUser, final String privateKeyPath, final byte[] passphrase) {
|
||||
final Session session = createSession(sshHost, sshPort, sshUser, privateKeyPath, passphrase);
|
||||
try {
|
||||
session.connect();
|
||||
} catch (final JSchException e) {
|
||||
throw new SshException(e);
|
||||
}
|
||||
return session;
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开一个新的SSH会话
|
||||
*
|
||||
* @param sshHost 主机
|
||||
* @param sshPort 端口
|
||||
* @param sshUser 用户名
|
||||
* @param privateKeyPath 私钥的路径
|
||||
* @param passphrase 私钥文件的密码,可以为null
|
||||
* @param timeout 超时时间,单位毫秒
|
||||
* @return SSH会话
|
||||
*/
|
||||
public static Session openSession(final String sshHost, final int sshPort, final String sshUser, final String privateKeyPath, final byte[] passphrase, final int timeout) {
|
||||
final Session session = createSession(sshHost, sshPort, sshUser, privateKeyPath, passphrase);
|
||||
try {
|
||||
session.setTimeout(timeout);
|
||||
session.connect(timeout);
|
||||
} catch (final JSchException e) {
|
||||
throw new SshException(e);
|
||||
}
|
||||
return session;
|
||||
}
|
||||
|
||||
/**
|
||||
* 新建一个新的SSH会话,此方法并不打开会话(既不调用connect方法)
|
||||
*
|
||||
* @param sshHost 主机
|
||||
* @param sshPort 端口
|
||||
* @param sshUser 用户名,如果为null,默认root
|
||||
* @param sshPass 密码
|
||||
* @return SSH会话
|
||||
* @since 4.5.2
|
||||
*/
|
||||
public static Session createSession(final String sshHost, final int sshPort, final String sshUser, final String sshPass) {
|
||||
final JSch jsch = new JSch();
|
||||
final Session session = createSession(jsch, sshHost, sshPort, sshUser);
|
||||
|
||||
if (StrUtil.isNotEmpty(sshPass)) {
|
||||
session.setPassword(sshPass);
|
||||
}
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
/**
|
||||
* 新建一个新的SSH会话,此方法并不打开会话(既不调用connect方法)
|
||||
*
|
||||
* @param sshHost 主机
|
||||
* @param sshPort 端口
|
||||
* @param sshUser 用户名,如果为null,默认root
|
||||
* @param privateKeyPath 私钥的路径
|
||||
* @param passphrase 私钥文件的密码,可以为null
|
||||
* @return SSH会话
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public static Session createSession(final String sshHost, final int sshPort, final String sshUser, final String privateKeyPath, final byte[] passphrase) {
|
||||
Assert.notEmpty(privateKeyPath, "PrivateKey Path must be not empty!");
|
||||
|
||||
final JSch jsch = new JSch();
|
||||
try {
|
||||
jsch.addIdentity(privateKeyPath, passphrase);
|
||||
} catch (final JSchException e) {
|
||||
throw new SshException(e);
|
||||
}
|
||||
|
||||
return createSession(jsch, sshHost, sshPort, sshUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个SSH会话,重用已经使用的会话
|
||||
*
|
||||
* @param jsch {@link JSch}
|
||||
* @param sshHost 主机
|
||||
* @param sshPort 端口
|
||||
* @param sshUser 用户名,如果为null,默认root
|
||||
* @return {@link Session}
|
||||
* @since 5.0.3
|
||||
*/
|
||||
public static Session createSession(JSch jsch, final String sshHost, final int sshPort, String sshUser) {
|
||||
Assert.notEmpty(sshHost, "SSH Host must be not empty!");
|
||||
Assert.isTrue(sshPort > 0, "SSH port must be > 0");
|
||||
|
||||
// 默认root用户
|
||||
if (StrUtil.isEmpty(sshUser)) {
|
||||
sshUser = "root";
|
||||
}
|
||||
|
||||
if (null == jsch) {
|
||||
jsch = new JSch();
|
||||
}
|
||||
|
||||
final Session session;
|
||||
try {
|
||||
session = jsch.getSession(sshUser, sshHost, sshPort);
|
||||
} catch (final JSchException e) {
|
||||
throw new SshException(e);
|
||||
}
|
||||
|
||||
// 设置第一次登录的时候提示,可选值:(ask | yes | no)
|
||||
session.setConfig("StrictHostKeyChecking", "no");
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定端口到本地。 一个会话可绑定多个端口
|
||||
*
|
||||
* @param session 需要绑定端口的SSH会话
|
||||
* @param remoteHost 远程主机
|
||||
* @param remotePort 远程端口
|
||||
* @param localPort 本地端口
|
||||
* @return 成功与否
|
||||
* @throws SshException 端口绑定失败异常
|
||||
*/
|
||||
public static boolean bindPort(final Session session, final String remoteHost, final int remotePort, final int localPort) throws SshException {
|
||||
return bindPort(session, remoteHost, remotePort, "127.0.0.1", localPort);
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定端口到本地。 一个会话可绑定多个端口
|
||||
*
|
||||
* @param session 需要绑定端口的SSH会话
|
||||
* @param remoteHost 远程主机
|
||||
* @param remotePort 远程端口
|
||||
* @param localHost 本地主机
|
||||
* @param localPort 本地端口
|
||||
* @return 成功与否
|
||||
* @throws SshException 端口绑定失败异常
|
||||
* @since 5.7.8
|
||||
*/
|
||||
public static boolean bindPort(final Session session, final String remoteHost, final int remotePort, final String localHost, final int localPort) throws SshException {
|
||||
if (session != null && session.isConnected()) {
|
||||
try {
|
||||
session.setPortForwardingL(localHost, localPort, remoteHost, remotePort);
|
||||
} catch (final JSchException e) {
|
||||
throw new SshException(e, "From [{}:{}] mapping to [{}:{}] error!", remoteHost, remotePort, localHost, localPort);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 绑定ssh服务端的serverPort端口, 到host主机的port端口上. <br>
|
||||
* 即数据从ssh服务端的serverPort端口, 流经ssh客户端, 达到host:port上.
|
||||
*
|
||||
* @param session 与ssh服务端建立的会话
|
||||
* @param bindPort ssh服务端上要被绑定的端口
|
||||
* @param host 转发到的host
|
||||
* @param port host上的端口
|
||||
* @return 成功与否
|
||||
* @throws SshException 端口绑定失败异常
|
||||
* @since 5.4.2
|
||||
*/
|
||||
public static boolean bindRemotePort(final Session session, final int bindPort, final String host, final int port) throws SshException {
|
||||
if (session != null && session.isConnected()) {
|
||||
try {
|
||||
session.setPortForwardingR(bindPort, host, port);
|
||||
} catch (final JSchException e) {
|
||||
throw new SshException(e, "From [{}] mapping to [{}] error!", bindPort, port);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 解除端口映射
|
||||
*
|
||||
* @param session 需要解除端口映射的SSH会话
|
||||
* @param localPort 需要解除的本地端口
|
||||
*/
|
||||
public static void unBindPort(final Session session, final int localPort) {
|
||||
try {
|
||||
session.delPortForwardingL(localPort);
|
||||
} catch (final JSchException e) {
|
||||
throw new SshException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开SSH会话,并绑定远程端口到本地的一个随机端口
|
||||
*
|
||||
* @param sshConn SSH连接信息对象
|
||||
* @param remoteHost 远程主机
|
||||
* @param remotePort 远程端口
|
||||
* @return 映射后的本地端口
|
||||
* @throws SshException 连接异常
|
||||
*/
|
||||
public static int openAndBindPortToLocal(final Connector sshConn, final String remoteHost, final int remotePort) throws SshException {
|
||||
final Session session = openSession(sshConn.getHost(), sshConn.getPort(), sshConn.getUser(), sshConn.getPassword());
|
||||
final int localPort = generateLocalPort();
|
||||
bindPort(session, remoteHost, remotePort, localPort);
|
||||
return localPort;
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开SFTP连接
|
||||
*
|
||||
* @param session Session会话
|
||||
* @return {@link ChannelSftp}
|
||||
* @since 4.0.3
|
||||
*/
|
||||
public static ChannelSftp openSftp(final Session session) {
|
||||
return openSftp(session, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开SFTP连接
|
||||
*
|
||||
* @param session Session会话
|
||||
* @param timeout 连接超时时长,单位毫秒
|
||||
* @return {@link ChannelSftp}
|
||||
* @since 5.3.3
|
||||
*/
|
||||
public static ChannelSftp openSftp(final Session session, final int timeout) {
|
||||
return (ChannelSftp) openChannel(session, ChannelType.SFTP, timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建Sftp
|
||||
*
|
||||
* @param sshHost 远程主机
|
||||
* @param sshPort 远程主机端口
|
||||
* @param sshUser 远程主机用户名
|
||||
* @param sshPass 远程主机密码
|
||||
* @return {@link Sftp}
|
||||
* @since 4.0.3
|
||||
*/
|
||||
public static Sftp createSftp(final String sshHost, final int sshPort, final String sshUser, final String sshPass) {
|
||||
return new Sftp(sshHost, sshPort, sshUser, sshPass);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建Sftp
|
||||
*
|
||||
* @param session SSH会话
|
||||
* @return {@link Sftp}
|
||||
* @since 4.0.5
|
||||
*/
|
||||
public static Sftp createSftp(final Session session) {
|
||||
return new Sftp(session);
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开Shell连接
|
||||
*
|
||||
* @param session Session会话
|
||||
* @return {@link ChannelShell}
|
||||
* @since 4.0.3
|
||||
*/
|
||||
public static ChannelShell openShell(final Session session) {
|
||||
return (ChannelShell) openChannel(session, ChannelType.SHELL);
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开Channel连接
|
||||
*
|
||||
* @param session Session会话
|
||||
* @param channelType 通道类型,可以是shell或sftp等,见{@link ChannelType}
|
||||
* @return {@link Channel}
|
||||
* @since 4.5.2
|
||||
*/
|
||||
public static Channel openChannel(final Session session, final ChannelType channelType) {
|
||||
return openChannel(session, channelType, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开Channel连接
|
||||
*
|
||||
* @param session Session会话
|
||||
* @param channelType 通道类型,可以是shell或sftp等,见{@link ChannelType}
|
||||
* @param timeout 连接超时时长,单位毫秒
|
||||
* @return {@link Channel}
|
||||
* @since 5.3.3
|
||||
*/
|
||||
public static Channel openChannel(final Session session, final ChannelType channelType, final int timeout) {
|
||||
final Channel channel = createChannel(session, channelType);
|
||||
try {
|
||||
channel.connect(Math.max(timeout, 0));
|
||||
} catch (final JSchException e) {
|
||||
throw new SshException(e);
|
||||
}
|
||||
return channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建Channel连接
|
||||
*
|
||||
* @param session Session会话
|
||||
* @param channelType 通道类型,可以是shell或sftp等,见{@link ChannelType}
|
||||
* @return {@link Channel}
|
||||
* @since 4.5.2
|
||||
*/
|
||||
public static Channel createChannel(final Session session, final ChannelType channelType) {
|
||||
final Channel channel;
|
||||
try {
|
||||
if (!session.isConnected()) {
|
||||
session.connect();
|
||||
}
|
||||
channel = session.openChannel(channelType.getValue());
|
||||
} catch (final JSchException e) {
|
||||
throw new SshException(e);
|
||||
}
|
||||
return channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行Shell命令
|
||||
*
|
||||
* @param session Session会话
|
||||
* @param cmd 命令
|
||||
* @param charset 发送和读取内容的编码
|
||||
* @return {@link ChannelExec}
|
||||
* @since 4.0.3
|
||||
*/
|
||||
public static String exec(final Session session, final String cmd, final Charset charset) {
|
||||
return exec(session, cmd, charset, System.err);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行Shell命令(使用EXEC方式)
|
||||
* <p>
|
||||
* 此方法单次发送一个命令到服务端,不读取环境变量,执行结束后自动关闭channel,不会产生阻塞。
|
||||
* </p>
|
||||
*
|
||||
* @param session Session会话
|
||||
* @param cmd 命令
|
||||
* @param charset 发送和读取内容的编码
|
||||
* @param errStream 错误信息输出到的位置
|
||||
* @return 执行结果内容
|
||||
* @since 4.3.1
|
||||
*/
|
||||
public static String exec(final Session session, final String cmd, Charset charset, final OutputStream errStream) {
|
||||
if (null == charset) {
|
||||
charset = CharsetUtil.UTF_8;
|
||||
}
|
||||
final ChannelExec channel = (ChannelExec) createChannel(session, ChannelType.EXEC);
|
||||
channel.setCommand(ByteUtil.toBytes(cmd, charset));
|
||||
channel.setInputStream(null);
|
||||
channel.setErrStream(errStream);
|
||||
InputStream in = null;
|
||||
try {
|
||||
channel.connect();
|
||||
in = channel.getInputStream();
|
||||
return IoUtil.read(in, charset);
|
||||
} catch (final IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
} catch (final JSchException e) {
|
||||
throw new SshException(e);
|
||||
} finally {
|
||||
IoUtil.closeQuietly(in);
|
||||
close(channel);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行Shell命令
|
||||
* <p>
|
||||
* 此方法单次发送一个命令到服务端,自动读取环境变量,执行结束后自动关闭channel,不会产生阻塞。
|
||||
* </p>
|
||||
*
|
||||
* @param session Session会话
|
||||
* @param cmd 命令
|
||||
* @param charset 发送和读取内容的编码
|
||||
* @return {@link ChannelExec}
|
||||
* @since 5.2.5
|
||||
*/
|
||||
public static String execByShell(final Session session, final String cmd, final Charset charset) {
|
||||
final ChannelShell shell = openShell(session);
|
||||
// 开始连接
|
||||
shell.setPty(true);
|
||||
OutputStream out = null;
|
||||
InputStream in = null;
|
||||
try {
|
||||
out = shell.getOutputStream();
|
||||
in = shell.getInputStream();
|
||||
|
||||
out.write(ByteUtil.toBytes(cmd, charset));
|
||||
out.flush();
|
||||
|
||||
return IoUtil.read(in, charset);
|
||||
} catch (final IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
} finally {
|
||||
IoUtil.closeQuietly(out);
|
||||
IoUtil.closeQuietly(in);
|
||||
close(shell);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭SSH连接会话
|
||||
*
|
||||
* @param session SSH会话
|
||||
*/
|
||||
public static void close(final Session session) {
|
||||
if (session != null && session.isConnected()) {
|
||||
session.disconnect();
|
||||
}
|
||||
JschSessionPool.INSTANCE.remove(session);
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭会话通道
|
||||
*
|
||||
* @param channel 会话通道
|
||||
* @since 4.0.3
|
||||
*/
|
||||
public static void close(final Channel channel) {
|
||||
if (channel != null && channel.isConnected()) {
|
||||
channel.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭SSH连接会话
|
||||
*
|
||||
* @param key 主机,格式为user@host:port
|
||||
*/
|
||||
public static void close(final String key) {
|
||||
JschSessionPool.INSTANCE.close(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭所有SSH连接会话
|
||||
*/
|
||||
public static void closeAll() {
|
||||
JschSessionPool.INSTANCE.closeAll();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright (c) 2023 looly(loolly@aliyun.com)
|
||||
* Hutool is licensed under Mulan PSL v2.
|
||||
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
* You may obtain a copy of Mulan PSL v2 at:
|
||||
* https://license.coscl.org.cn/MulanPSL2
|
||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
* See the Mulan PSL v2 for more details.
|
||||
*/
|
||||
|
||||
package org.dromara.hutool.extra.ssh.engine.sshj;
|
||||
|
||||
import net.schmizz.sshj.SSHClient;
|
||||
import net.schmizz.sshj.transport.verification.PromiscuousVerifier;
|
||||
import org.dromara.hutool.core.io.IORuntimeException;
|
||||
import org.dromara.hutool.core.io.IoUtil;
|
||||
import org.dromara.hutool.core.util.CharsetUtil;
|
||||
import org.dromara.hutool.extra.ssh.Connector;
|
||||
import org.dromara.hutool.extra.ssh.Session;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
/**
|
||||
* 基于SSHJ(https://github.com/hierynomus/sshj)的Session封装
|
||||
*
|
||||
* @author looly
|
||||
*/
|
||||
public class SshjSession implements Session {
|
||||
|
||||
private SSHClient ssh;
|
||||
private final net.schmizz.sshj.connection.channel.direct.Session raw;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param connector {@link Connector},保存连接和验证信息等
|
||||
*/
|
||||
public SshjSession(final Connector connector) {
|
||||
final SSHClient ssh = new SSHClient();
|
||||
ssh.addHostKeyVerifier(new PromiscuousVerifier());
|
||||
try {
|
||||
ssh.connect(connector.getHost(), connector.getPort());
|
||||
ssh.authPassword(connector.getUser(), connector.getPassword());
|
||||
this.raw = ssh.startSession();
|
||||
} catch (final IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
}
|
||||
|
||||
this.ssh = ssh;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param raw {@link net.schmizz.sshj.connection.channel.direct.Session}
|
||||
*/
|
||||
public SshjSession(final net.schmizz.sshj.connection.channel.direct.Session raw) {
|
||||
this.raw = raw;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getRaw() {
|
||||
return raw;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
IoUtil.closeQuietly(this.raw);
|
||||
IoUtil.closeQuietly(this.ssh);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行Shell命令(使用EXEC方式)
|
||||
* <p>
|
||||
* 此方法单次发送一个命令到服务端,不读取环境变量,不会产生阻塞。
|
||||
* </p>
|
||||
*
|
||||
* @param cmd 命令
|
||||
* @param charset 发送和读取内容的编码
|
||||
* @param errStream 错误信息输出到的位置
|
||||
* @return 执行返回结果
|
||||
*/
|
||||
public String exec(final String cmd, Charset charset, final OutputStream errStream) {
|
||||
if (null == charset) {
|
||||
charset = CharsetUtil.UTF_8;
|
||||
}
|
||||
|
||||
final net.schmizz.sshj.connection.channel.direct.Session.Command command;
|
||||
|
||||
// 发送命令
|
||||
try {
|
||||
command = this.raw.exec(cmd);
|
||||
//command.join();
|
||||
} catch (final IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
}
|
||||
|
||||
// 错误输出
|
||||
if (null != errStream) {
|
||||
IoUtil.copy(command.getErrorStream(), errStream);
|
||||
}
|
||||
|
||||
// 结果输出
|
||||
return IoUtil.read(command.getInputStream(), charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行Shell命令
|
||||
* <p>
|
||||
* 此方法单次发送一个命令到服务端,自动读取环境变量,可能产生阻塞。
|
||||
* </p>
|
||||
*
|
||||
* @param cmd 命令
|
||||
* @param charset 发送和读取内容的编码
|
||||
* @param errStream 错误信息输出到的位置
|
||||
* @return 执行返回结果
|
||||
*/
|
||||
public String execByShell(final String cmd, Charset charset, final OutputStream errStream) {
|
||||
if (null == charset) {
|
||||
charset = CharsetUtil.UTF_8;
|
||||
}
|
||||
|
||||
final net.schmizz.sshj.connection.channel.direct.Session.Shell shell;
|
||||
try {
|
||||
shell = this.raw.startShell();
|
||||
} catch (final IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
}
|
||||
|
||||
// 发送命令
|
||||
IoUtil.write(shell.getOutputStream(), charset, true, cmd);
|
||||
|
||||
// 错误输出
|
||||
if (null != errStream) {
|
||||
IoUtil.copy(shell.getErrorStream(), errStream);
|
||||
}
|
||||
|
||||
// 结果输出
|
||||
return IoUtil.read(shell.getInputStream(), charset);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user