diff --git a/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/Session.java b/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/Session.java index 4f5a588d3..693db4c4d 100644 --- a/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/Session.java +++ b/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/Session.java @@ -13,8 +13,10 @@ package org.dromara.hutool.extra.ssh; import org.dromara.hutool.core.func.Wrapper; +import org.dromara.hutool.core.io.IORuntimeException; import java.io.Closeable; +import java.net.InetSocketAddress; /** * SSH Session抽象 @@ -29,4 +31,47 @@ public interface Session extends Wrapper, Closeable { * @return 是否连接状态 */ boolean isConnected(); + + // region bindPort + /** + * 绑定端口到本地。 一个会话可绑定多个端口
+ * 当请求localHost:localPort时,通过SSH到服务器,转发请求到remoteHost:remotePort
+ * 此方法用于访问本地无法访问但是服务器可以访问的地址,如内网数据库库等 + * + * @param localPort 本地端口 + * @param remoteAddress 远程主机和端口 + * @return 成功与否 + */ + default boolean bindLocalPort(final int localPort, final InetSocketAddress remoteAddress) { + return bindLocalPort(new InetSocketAddress(localPort), remoteAddress); + } + + /** + * 绑定端口到本地。 一个会话可绑定多个端口
+ * 当请求localHost:localPort时,通过SSH到服务器,转发请求到remoteHost:remotePort
+ * 此方法用于访问本地无法访问但是服务器可以访问的地址,如内网数据库库等 + * + * @param localAddress 本地主机和端口 + * @param remoteAddress 远程主机和端口 + * @return 成功与否 + */ + boolean bindLocalPort(final InetSocketAddress localAddress, final InetSocketAddress remoteAddress); + + /** + * 解除本地端口映射 + * + * @param localPort 需要解除的本地端口 + * @throws IORuntimeException 端口解绑失败异常 + */ + default void unBindLocalPort(final int localPort){ + unBindLocalPort(new InetSocketAddress(localPort)); + } + + /** + * 解除本地端口映射 + * + * @param localAddress 需要解除的本地地址 + */ + void unBindLocalPort(final InetSocketAddress localAddress); + // endregion } diff --git a/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/engine/ganymed/GanymedSession.java b/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/engine/ganymed/GanymedSession.java index e5173bf10..24153f5df 100644 --- a/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/engine/ganymed/GanymedSession.java +++ b/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/engine/ganymed/GanymedSession.java @@ -40,7 +40,7 @@ public class GanymedSession implements Session { private Connection connection; private final ch.ethz.ssh2.Session raw; - private Map localPortForwarderMap; + private Map localPortForwarderMap; /** * 构造 @@ -91,37 +91,11 @@ public class GanymedSession implements Session { } } - /** - * 绑定端口到本地。 一个会话可绑定多个端口
- * 当请求localHost:localPort时,通过SSH到服务器,转发请求到remoteHost:remotePort
- * 此方法用于访问本地无法访问但是服务器可以访问的地址,如内网数据库库等 - * - * @param remoteHost 远程主机 - * @param remotePort 远程端口 - * @param localPort 本地端口 - * @return 成功与否 - * @throws IORuntimeException 端口绑定失败异常 - */ - public boolean bindLocalPort(final String remoteHost, final int remotePort, final int localPort) throws IORuntimeException { - return bindLocalPort(remoteHost, remotePort, Ipv4Util.LOCAL_IP, localPort); - } - - /** - * 绑定端口到本地。 一个会话可绑定多个端口
- * 当请求localHost:localPort时,通过SSH到服务器,转发请求到remoteHost:remotePort
- * 此方法用于访问本地无法访问但是服务器可以访问的地址,如内网数据库库等 - * - * @param remoteHost 远程主机 - * @param remotePort 远程端口 - * @param localHost 本地主机 - * @param localPort 本地端口 - * @return 成功与否 - * @throws IORuntimeException 端口绑定失败异常 - */ - public boolean bindLocalPort(final String remoteHost, final int remotePort, final String localHost, final int localPort) throws IORuntimeException { + @Override + public boolean bindLocalPort(final InetSocketAddress localAddress, final InetSocketAddress remoteAddress) throws IORuntimeException { final LocalPortForwarder localPortForwarder; try { - localPortForwarder = this.connection.createLocalPortForwarder(new InetSocketAddress(localHost, localPort), remoteHost, remotePort); + localPortForwarder = this.connection.createLocalPortForwarder(localAddress, remoteAddress.getHostName(), remoteAddress.getPort()); } catch (final IOException e) { throw new IORuntimeException(e); } @@ -131,23 +105,18 @@ public class GanymedSession implements Session { } //加入记录 - this.localPortForwarderMap.put(localPort, localPortForwarder); + this.localPortForwarderMap.put(localAddress.toString(), localPortForwarder); return true; } - /** - * 解除本地端口映射 - * - * @param localPort 需要解除的本地端口 - * @throws IORuntimeException 端口解绑失败异常 - */ - public void unBindLocalPort(final int localPort) throws IORuntimeException { + @Override + public void unBindLocalPort(final InetSocketAddress localAddress) throws IORuntimeException { if (MapUtil.isEmpty(this.localPortForwarderMap)) { return; } - final LocalPortForwarder localPortForwarder = this.localPortForwarderMap.remove(localPort); + final LocalPortForwarder localPortForwarder = this.localPortForwarderMap.remove(localAddress.toString()); if (null != localPortForwarder) { try { localPortForwarder.close(); diff --git a/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/engine/jsch/JschSession.java b/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/engine/jsch/JschSession.java index bc2d23f12..58da85efd 100644 --- a/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/engine/jsch/JschSession.java +++ b/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/engine/jsch/JschSession.java @@ -12,10 +12,12 @@ package org.dromara.hutool.extra.ssh.engine.jsch; -import com.jcraft.jsch.*; +import com.jcraft.jsch.Channel; +import com.jcraft.jsch.ChannelExec; +import com.jcraft.jsch.ChannelShell; +import com.jcraft.jsch.JSchException; import org.dromara.hutool.core.io.IORuntimeException; import org.dromara.hutool.core.io.IoUtil; -import org.dromara.hutool.core.net.Ipv4Util; import org.dromara.hutool.core.util.ByteUtil; import org.dromara.hutool.core.util.CharsetUtil; import org.dromara.hutool.extra.ssh.Connector; @@ -25,6 +27,7 @@ import org.dromara.hutool.extra.ssh.SshException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.net.InetSocketAddress; import java.nio.charset.Charset; /** @@ -70,53 +73,23 @@ public class JschSession implements Session { JschUtil.close(this.raw); } - /** - * 绑定端口到本地。 一个会话可绑定多个端口
- * 当请求localHost:localPort时,通过SSH到服务器,转发请求到remoteHost:remotePort
- * 此方法用于访问本地无法访问但是服务器可以访问的地址,如内网数据库库等 - * - * @param remoteHost 远程主机 - * @param remotePort 远程端口 - * @param localPort 本地端口 - * @return 成功与否 - * @throws SshException 端口绑定失败异常 - */ - public boolean bindLocalPort(final String remoteHost, final int remotePort, final int localPort) throws SshException { - return bindLocalPort(remoteHost, remotePort, Ipv4Util.LOCAL_IP, localPort); - } - - /** - * 绑定端口到本地。 一个会话可绑定多个端口
- * 当请求localHost:localPort时,通过SSH到服务器,转发请求到remoteHost:remotePort
- * 此方法用于访问本地无法访问但是服务器可以访问的地址,如内网数据库库等 - * - * @param remoteHost 远程主机 - * @param remotePort 远程端口 - * @param localHost 本地主机 - * @param localPort 本地端口 - * @return 成功与否 - * @throws SshException 端口绑定失败异常 - */ - public boolean bindLocalPort(final String remoteHost, final int remotePort, final String localHost, final int localPort) throws SshException { + @Override + public boolean bindLocalPort(final InetSocketAddress localAddress, final InetSocketAddress remoteAddress) throws SshException { if (isConnected()) { try { - this.raw.setPortForwardingL(localHost, localPort, remoteHost, remotePort); + this.raw.setPortForwardingL(localAddress.getHostName(), localAddress.getPort(), remoteAddress.getHostName(), remoteAddress.getPort()); } catch (final JSchException e) { - throw new SshException(e, "From [{}:{}] mapping to [{}:{}] error!", remoteHost, remotePort, localHost, localPort); + throw new SshException(e, "From [{}] mapping to [{}] error!", localAddress, remoteAddress); } return true; } return false; } - /** - * 解除远程端口映射 - * - * @param localPort 需要解除的本地端口 - */ - public void unBindLocalPort(final int localPort) { + @Override + public void unBindLocalPort(final InetSocketAddress localAddress) { try { - this.raw.delPortForwardingL(localPort); + this.raw.delPortForwardingL(localAddress.getHostName(), localAddress.getPort()); } catch (final JSchException e) { throw new SshException(e); } diff --git a/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/engine/mina/MinaSession.java b/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/engine/mina/MinaSession.java index f6d6aa5aa..7c83c89b7 100644 --- a/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/engine/mina/MinaSession.java +++ b/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/engine/mina/MinaSession.java @@ -15,6 +15,7 @@ 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.apache.sshd.common.util.net.SshdSocketAddress; import org.dromara.hutool.core.io.IORuntimeException; import org.dromara.hutool.core.io.IoUtil; import org.dromara.hutool.extra.ssh.Connector; @@ -22,6 +23,7 @@ import org.dromara.hutool.extra.ssh.Session; import java.io.IOException; import java.io.OutputStream; +import java.net.InetSocketAddress; import java.nio.charset.Charset; /** @@ -63,6 +65,25 @@ public class MinaSession implements Session { IoUtil.closeQuietly(this.sshClient); } + @Override + public boolean bindLocalPort(final InetSocketAddress localAddress, final InetSocketAddress remoteAddress) throws IORuntimeException { + try { + this.raw.startLocalPortForwarding(new SshdSocketAddress(localAddress), new SshdSocketAddress(remoteAddress)); + } catch (final IOException e) { + throw new IORuntimeException(e); + } + return true; + } + + @Override + public void unBindLocalPort(final InetSocketAddress localAddress) throws IORuntimeException { + try { + this.raw.stopLocalPortForwarding(new SshdSocketAddress(localAddress)); + } catch (final IOException e) { + throw new IORuntimeException(e); + } + } + /** * 执行Shell命令 * @@ -100,16 +121,16 @@ public class MinaSession implements Session { * 此方法单次发送一个命令到服务端,自动读取环境变量,执行结束后自动关闭channel,不会产生阻塞。 *

* - * @param cmd 命令 - * @param charset 发送和读取内容的编码 + * @param cmd 命令 + * @param charset 发送和读取内容的编码 * @param errStream 异常输出位置 * @return 结果 */ - public String execByShell(final String cmd, final Charset charset, final OutputStream errStream){ + public String execByShell(final String cmd, final Charset charset, final OutputStream errStream) { final ChannelShell shellChannel; try { shellChannel = this.raw.createShellChannel(); - if(null != errStream){ + if (null != errStream) { shellChannel.setErr(errStream); } shellChannel.open().verify(); @@ -118,6 +139,6 @@ public class MinaSession implements Session { } IoUtil.write(shellChannel.getInvertedIn(), charset, false, cmd); - return IoUtil.read(shellChannel.getInvertedOut(),charset); + return IoUtil.read(shellChannel.getInvertedOut(), charset); } } diff --git a/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/engine/sshj/SshjSession.java b/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/engine/sshj/SshjSession.java index 93847f289..8fd236d9a 100644 --- a/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/engine/sshj/SshjSession.java +++ b/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/engine/sshj/SshjSession.java @@ -19,11 +19,9 @@ import net.schmizz.sshj.connection.channel.forwarded.SocketForwardingConnectList import org.dromara.hutool.core.io.IORuntimeException; import org.dromara.hutool.core.io.IoUtil; import org.dromara.hutool.core.map.MapUtil; -import org.dromara.hutool.core.net.Ipv4Util; import org.dromara.hutool.core.util.CharsetUtil; import org.dromara.hutool.extra.ssh.Connector; import org.dromara.hutool.extra.ssh.Session; -import org.dromara.hutool.extra.ssh.SshException; import java.io.IOException; import java.io.OutputStream; @@ -44,7 +42,7 @@ public class SshjSession implements Session { private SSHClient ssh; private final net.schmizz.sshj.connection.channel.direct.Session raw; - private Map localPortForwarderMap; + private Map localPortForwarderMap; /** * 构造 @@ -95,40 +93,16 @@ public class SshjSession implements Session { return new SshjSftp(this.ssh, charset); } - /** - * 绑定端口到本地。 一个会话可绑定多个端口
- * 当请求localHost:localPort时,通过SSH到服务器,转发请求到remoteHost:remotePort
- * 此方法用于访问本地无法访问但是服务器可以访问的地址,如内网数据库库等 - * - * @param remoteHost 远程主机 - * @param remotePort 远程端口 - * @param localPort 本地端口 - * @return 成功与否 - * @throws SshException 端口绑定失败异常 - */ - public boolean bindLocalPort(final String remoteHost, final int remotePort, final int localPort) throws SshException { - return bindLocalPort(remoteHost, remotePort, Ipv4Util.LOCAL_IP, localPort); - } - - /** - * 绑定端口到本地。 一个会话可绑定多个端口
- * 当请求localHost:localPort时,通过SSH到服务器,转发请求到remoteHost:remotePort
- * 此方法用于访问本地无法访问但是服务器可以访问的地址,如内网数据库库等 - * - * @param remoteHost 远程主机 - * @param remotePort 远程端口 - * @param localHost 本地主机 - * @param localPort 本地端口 - * @return 成功与否 - * @throws IORuntimeException 端口绑定失败异常 - */ - public boolean bindLocalPort(final String remoteHost, final int remotePort, final String localHost, final int localPort) throws IORuntimeException { - final Parameters params = new Parameters(localHost, localPort, remoteHost, remotePort); + @Override + public boolean bindLocalPort(final InetSocketAddress localAddress, final InetSocketAddress remoteAddress) throws IORuntimeException { + final Parameters params = new Parameters( + localAddress.getHostName(), localAddress.getPort(), + remoteAddress.getHostName(), remoteAddress.getPort()); final ServerSocket ss; try { ss = new ServerSocket(); ss.setReuseAddress(true); - ss.bind(new InetSocketAddress(params.getLocalHost(), params.getLocalPort())); + ss.bind(localAddress); ssh.newLocalPortForwarder(params, ss).listen(); } catch (final IOException e) { throw new IORuntimeException(e); @@ -139,23 +113,18 @@ public class SshjSession implements Session { } //加入记录 - this.localPortForwarderMap.put(localPort, ss); + this.localPortForwarderMap.put(localAddress.toString(), ss); return true; } - /** - * 解除本地端口映射 - * - * @param localPort 需要解除的本地端口 - * @throws IORuntimeException 端口解绑失败异常 - */ - public void unBindLocalPort(final int localPort) throws IORuntimeException { + @Override + public void unBindLocalPort(final InetSocketAddress localAddress) throws IORuntimeException { if (MapUtil.isEmpty(this.localPortForwarderMap)) { return; } - IoUtil.closeQuietly(this.localPortForwarderMap.remove(localPort)); + IoUtil.closeQuietly(this.localPortForwarderMap.remove(localAddress.toString())); } /** diff --git a/hutool-extra/src/test/java/org/dromara/hutool/extra/ssh/JschTest.java b/hutool-extra/src/test/java/org/dromara/hutool/extra/ssh/JschTest.java index 543fda058..16c9dd919 100644 --- a/hutool-extra/src/test/java/org/dromara/hutool/extra/ssh/JschTest.java +++ b/hutool-extra/src/test/java/org/dromara/hutool/extra/ssh/JschTest.java @@ -21,6 +21,8 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import java.net.InetSocketAddress; + /** * Jsch工具类单元测试 * @@ -36,7 +38,7 @@ public class JschTest { //新建会话,此会话用于ssh连接到跳板机(堡垒机),此处为10.1.1.1:22 final JschSession session = new JschSession(new Connector("looly.centos", 22, "test", "123456")); // 将堡垒机保护的内网8080端口映射到localhost,我们就可以通过访问http://localhost:8080/访问内网服务了 - session.bindLocalPort("172.20.12.123", 8080, 8080); + session.bindLocalPort(8080, new InetSocketAddress("172.20.12.123", 8080)); } @SuppressWarnings("resource")