This commit is contained in:
Looly 2023-09-24 19:12:15 +08:00
parent 6b4c5ede6a
commit d27f91c083
6 changed files with 222 additions and 29 deletions

View File

@ -22,4 +22,11 @@ import java.io.Closeable;
* @author looly
*/
public interface Session extends Wrapper<Object>, Closeable {
/**
* 是否连接状态
*
* @return 是否连接状态
*/
boolean isConnected();
}

View File

@ -75,6 +75,12 @@ public class GanymedSession implements Session {
return raw;
}
@Override
public boolean isConnected() {
// 未找到合适的方法判断是否在线
return true;
}
@Override
public void close() throws IOException {
if (raw != null) {

View File

@ -60,12 +60,7 @@ public class JschSession implements Session {
return this.raw;
}
/**
* 是否连接状态
*
* @return 是否连接状态
*/
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
@Override
public boolean isConnected() {
return null != this.raw && this.raw.isConnected();
}
@ -103,16 +98,15 @@ public class JschSession implements Session {
* @throws SshException 端口绑定失败异常
*/
public boolean bindLocalPort(final String remoteHost, final int remotePort, final String localHost, final int localPort) throws SshException {
if (!isConnected()) {
return false;
if (isConnected()) {
try {
this.raw.setPortForwardingL(localHost, localPort, remoteHost, remotePort);
} catch (final JSchException e) {
throw new SshException(e, "From [{}:{}] mapping to [{}:{}] error", remoteHost, remotePort, localHost, localPort);
}
return true;
}
try {
this.raw.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;
}
/**
@ -139,16 +133,15 @@ public class JschSession implements Session {
* @throws SshException 端口绑定失败异常
*/
public boolean bindRemotePort(final int bindPort, final String host, final int port) throws SshException {
if (!isConnected()) {
return false;
if (isConnected()) {
try {
this.raw.setPortForwardingR(bindPort, host, port);
} catch (final JSchException e) {
throw new SshException(e, "From [{}] mapping to [{}] error", bindPort, port);
}
return true;
}
try {
this.raw.setPortForwardingR(bindPort, host, port);
} catch (final JSchException e) {
throw new SshException(e, "From [{}] mapping to [{}] error", bindPort, port);
}
return true;
return false;
}
/**

View File

@ -0,0 +1,123 @@
/*
* 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.mina;
import org.apache.sshd.client.SshClient;
import org.apache.sshd.client.channel.ChannelShell;
import org.apache.sshd.client.session.ClientSession;
import org.dromara.hutool.core.io.IORuntimeException;
import org.dromara.hutool.core.io.IoUtil;
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;
/**
* Apache MINA SSHDhttps://mina.apache.org/sshd-project/会话封装
*
* @author looly
*/
public class MinaSession implements Session {
private final SshClient sshClient;
private final ClientSession raw;
/**
* 构造
*
* @param connector 连接信息
*/
public MinaSession(final Connector connector) {
this.sshClient = MinaUtil.openClient();
this.raw = MinaUtil.openSession(this.sshClient, connector);
}
@Override
public Object getRaw() {
return this.raw;
}
@Override
public boolean isConnected() {
return null != this.raw && this.raw.isOpen();
}
@Override
public void close() throws IOException {
IoUtil.closeQuietly(this.raw);
if (null != this.sshClient) {
this.sshClient.stop();
}
IoUtil.closeQuietly(this.sshClient);
}
/**
* 执行Shell命令
*
* @param cmd 命令
* @param charset 发送和读取内容的编码
* @return 结果
*/
public String exec(final String cmd, final Charset charset) {
return exec(cmd, charset, System.err);
}
/**
* 执行Shell命令使用EXEC方式
* <p>
* 此方法单次发送一个命令到服务端不读取环境变量执行结束后自动关闭channel不会产生阻塞
* </p>
* 参考https://github.com/apache/mina-sshd/blob/master/docs/client-setup.md#running-a-command-or-opening-a-shell
*
* @param cmd 命令
* @param charset 发送和读取内容的编码
* @param errStream 错误信息输出到的位置
* @return 执行结果内容
*/
public String exec(final String cmd, final Charset charset, final OutputStream errStream) {
try {
return this.raw.executeRemoteCommand(cmd, errStream, charset);
} catch (final IOException e) {
throw new IORuntimeException(e);
}
}
/**
* 执行Shell命令
* <p>
* 此方法单次发送一个命令到服务端自动读取环境变量执行结束后自动关闭channel不会产生阻塞
* </p>
*
* @param cmd 命令
* @param charset 发送和读取内容的编码
* @param errStream 异常输出位置
* @return 结果
*/
public String execByShell(final String cmd, final Charset charset, final OutputStream errStream){
final ChannelShell shellChannel;
try {
shellChannel = this.raw.createShellChannel();
if(null != errStream){
shellChannel.setErr(errStream);
}
shellChannel.open().verify();
} catch (final IOException e) {
throw new IORuntimeException(e);
}
IoUtil.write(shellChannel.getInvertedIn(), charset, false, cmd);
return IoUtil.read(shellChannel.getInvertedOut(),charset);
}
}

View File

@ -0,0 +1,68 @@
/*
* 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.mina;
import org.apache.sshd.client.SshClient;
import org.apache.sshd.client.session.ClientSession;
import org.dromara.hutool.core.io.IORuntimeException;
import org.dromara.hutool.extra.ssh.Connector;
import java.io.IOException;
/**
* Apache MINA SSHDhttps://mina.apache.org/sshd-project/相关工具类
*
* @author looly
*/
public class MinaUtil {
/**
* 打开一个客户端对象
* @return 客户端对象
*/
public static SshClient openClient(){
final SshClient sshClient = SshClient.setUpDefaultClient();
sshClient.start();
return sshClient;
}
/**
* 打开一个新的Session
*
* @param sshClient 客户端
* @param connector 连接信息
* @return {@link ClientSession}
*/
public static ClientSession openSession(final SshClient sshClient, final Connector connector){
final ClientSession session;
final boolean success;
try {
session = sshClient
.connect(connector.getUser(), connector.getHost(), connector.getPort())
.verify()
.getSession();
session.addPasswordIdentity(connector.getPassword());
success = session.auth().verify().isSuccess();
} catch (final IOException e) {
throw new IORuntimeException(e);
}
if(!success){
throw new IORuntimeException("Authentication failed.");
}
return session;
}
}

View File

@ -74,11 +74,7 @@ public class SshjSession implements Session {
return raw;
}
/**
* 是否连接状态
*
* @return 是否连接状态
*/
@Override
public boolean isConnected() {
return null != this.raw && (null == this.ssh || this.ssh.isConnected());
}