From da1d060a084003b88bd29079b446cb2d23a65fab Mon Sep 17 00:00:00 2001 From: lzq Date: Tue, 28 Oct 2025 11:08:12 +0800 Subject: [PATCH] =?UTF-8?q?ssh=20=E9=9A=A7=E9=81=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mp/config/DBTunnelAutoConfiguration.java | 19 --- .../common/mp/config/DbTunnelProperties.java | 34 ----- .../njzscloud/common/mp/support/DBTunnel.java | 108 --------------- .../main/resources/META-INF/spring.factories | 3 +- .../njzscloud-common-sshtunnel/pom.xml | 12 +- .../njzscloud/common/sshtunnel/SSHTunnel.java | 129 ++++++++++++++++++ .../sshtunnel/SSHTunnelAutoConfiguration.java | 18 +++ .../common/sshtunnel/SSHTunnelProperties.java | 66 +++++++++ .../main/resources/META-INF/spring.factories | 2 + njzscloud-svr/pom.xml | 4 + .../src/main/resources/application-dev.yml | 29 ++-- pom.xml | 5 + 12 files changed, 248 insertions(+), 181 deletions(-) delete mode 100644 njzscloud-common/njzscloud-common-mp/src/main/java/com/njzscloud/common/mp/config/DBTunnelAutoConfiguration.java delete mode 100644 njzscloud-common/njzscloud-common-mp/src/main/java/com/njzscloud/common/mp/config/DbTunnelProperties.java delete mode 100644 njzscloud-common/njzscloud-common-mp/src/main/java/com/njzscloud/common/mp/support/DBTunnel.java create mode 100644 njzscloud-common/njzscloud-common-sshtunnel/src/main/java/com/njzscloud/common/sshtunnel/SSHTunnel.java create mode 100644 njzscloud-common/njzscloud-common-sshtunnel/src/main/java/com/njzscloud/common/sshtunnel/SSHTunnelAutoConfiguration.java create mode 100644 njzscloud-common/njzscloud-common-sshtunnel/src/main/java/com/njzscloud/common/sshtunnel/SSHTunnelProperties.java create mode 100644 njzscloud-common/njzscloud-common-sshtunnel/src/main/resources/META-INF/spring.factories diff --git a/njzscloud-common/njzscloud-common-mp/src/main/java/com/njzscloud/common/mp/config/DBTunnelAutoConfiguration.java b/njzscloud-common/njzscloud-common-mp/src/main/java/com/njzscloud/common/mp/config/DBTunnelAutoConfiguration.java deleted file mode 100644 index bf11311..0000000 --- a/njzscloud-common/njzscloud-common-mp/src/main/java/com/njzscloud/common/mp/config/DBTunnelAutoConfiguration.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.njzscloud.common.mp.config; - -import com.njzscloud.common.mp.support.DBTunnel; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration(proxyBeanMethods = false) -@ConditionalOnProperty(prefix = "mybatis-plus.tunnel", name = "enable", havingValue = "true") -@EnableConfigurationProperties({DbTunnelProperties.class}) -public class DBTunnelAutoConfiguration { - - @Bean(destroyMethod = "close") - public DBTunnel dbTunnel(DbTunnelProperties dbTunnelProperties) { - return new DBTunnel(dbTunnelProperties); - } - -} diff --git a/njzscloud-common/njzscloud-common-mp/src/main/java/com/njzscloud/common/mp/config/DbTunnelProperties.java b/njzscloud-common/njzscloud-common-mp/src/main/java/com/njzscloud/common/mp/config/DbTunnelProperties.java deleted file mode 100644 index bb56785..0000000 --- a/njzscloud-common/njzscloud-common-mp/src/main/java/com/njzscloud/common/mp/config/DbTunnelProperties.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.njzscloud.common.mp.config; - -import lombok.Getter; -import lombok.Setter; -import org.springframework.boot.context.properties.ConfigurationProperties; - -@Getter -@Setter -@ConfigurationProperties("mybatis-plus.tunnel") -public class DbTunnelProperties { - private boolean enable = false; - - private SSHProperties ssh; - private DBProperties db; - - @Getter - @Setter - public static class SSHProperties { - private String host; - private int port = 22; - private String user; - private String credentials; - private String passphrase; - private int localPort; - } - - @Getter - @Setter - public static class DBProperties { - private String host; - private int port = 3306; - } - -} diff --git a/njzscloud-common/njzscloud-common-mp/src/main/java/com/njzscloud/common/mp/support/DBTunnel.java b/njzscloud-common/njzscloud-common-mp/src/main/java/com/njzscloud/common/mp/support/DBTunnel.java deleted file mode 100644 index 75f3d65..0000000 --- a/njzscloud-common/njzscloud-common-mp/src/main/java/com/njzscloud/common/mp/support/DBTunnel.java +++ /dev/null @@ -1,108 +0,0 @@ -package com.njzscloud.common.mp.support; - -import com.jcraft.jsch.JSch; -import com.jcraft.jsch.JSchException; -import com.jcraft.jsch.Session; -import com.njzscloud.common.mp.config.DbTunnelProperties; -import lombok.extern.slf4j.Slf4j; - -import java.io.File; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; - -@Slf4j -public class DBTunnel { - - private final DbTunnelProperties dbTunnelProperties; - private JSch jsch; - private Session session; - private ScheduledExecutorService heartbeatExecutor; - - public DBTunnel(DbTunnelProperties dbTunnelProperties) { - this.dbTunnelProperties = dbTunnelProperties; - if (dbTunnelProperties.isEnable()) { - log.info("数据库 SSH 隧道已启用"); - connect(); - startHeartbeat(); - } - } - - private synchronized void connect() { - try { - if (isConnected()) { - return; - } - - DbTunnelProperties.SSHProperties ssh = dbTunnelProperties.getSsh(); - String credentials = ssh.getCredentials(); - String passphrase = ssh.getPassphrase(); - String user = ssh.getUser(); - String host = ssh.getHost(); - int port = ssh.getPort(); - int localPort = ssh.getLocalPort(); - DbTunnelProperties.DBProperties db = dbTunnelProperties.getDb(); - String dbHost = db.getHost(); - int dbPort = db.getPort(); - - jsch = new JSch(); - // 使用私钥认证 - File keyFile = new File(credentials); - if (!keyFile.exists()) { - throw new RuntimeException("私钥文件不存在: " + credentials); - } - - if (passphrase != null) { - jsch.addIdentity(credentials, passphrase); - } else { - jsch.addIdentity(credentials); - } - - // 创建SSH会话 - session = jsch.getSession(user, host, port); - session.setConfig("StrictHostKeyChecking", "no"); // 禁用主机密钥检查 - - // 设置保持活动状态 - session.setServerAliveInterval(60000); // 每分钟发送一次保持活动的数据包 - session.setServerAliveCountMax(3); // 允许3次失败 - - session.connect(); - - // 设置端口转发 (本地端口 -> 远程数据库) - session.setPortForwardingL(localPort, dbHost, dbPort); - log.info("SSH 隧道已成功连接并转发端口 {} -> {}:{}", localPort, dbHost, dbPort); - } catch (JSchException e) { - log.error("SSH 隧道连接失败", e); - throw new RuntimeException("SSH 隧道连接失败", e); - } - } - - private void startHeartbeat() { - heartbeatExecutor = Executors.newSingleThreadScheduledExecutor(); - heartbeatExecutor.scheduleAtFixedRate(() -> { - try { - if (!isConnected()) { - log.warn("SSH 隧道连接已断开,尝试重新连接..."); - connect(); - } - } catch (Exception e) { - log.error("SSH 隧道心跳检测失败", e); - } - }, 30, 30, TimeUnit.SECONDS); // 每30秒检查一次连接状态 - } - - public boolean isConnected() { - return session != null && session.isConnected(); - } - - public void close() { - if (heartbeatExecutor != null) { - heartbeatExecutor.shutdownNow(); - } - - if (session != null && session.isConnected()) { - session.disconnect(); - log.info("SSH隧道已关闭"); - } - } -} diff --git a/njzscloud-common/njzscloud-common-mp/src/main/resources/META-INF/spring.factories b/njzscloud-common/njzscloud-common-mp/src/main/resources/META-INF/spring.factories index 4240fc4..7d2ad42 100644 --- a/njzscloud-common/njzscloud-common-mp/src/main/resources/META-INF/spring.factories +++ b/njzscloud-common/njzscloud-common-mp/src/main/resources/META-INF/spring.factories @@ -1,3 +1,2 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration = \ - com.njzscloud.common.mp.config.MpAutoConfiguration,\ - com.njzscloud.common.mp.config.DBTunnelAutoConfiguration + com.njzscloud.common.mp.config.MpAutoConfiguration diff --git a/njzscloud-common/njzscloud-common-sshtunnel/pom.xml b/njzscloud-common/njzscloud-common-sshtunnel/pom.xml index 0b47583..8dbc309 100644 --- a/njzscloud-common/njzscloud-common-sshtunnel/pom.xml +++ b/njzscloud-common/njzscloud-common-sshtunnel/pom.xml @@ -19,10 +19,14 @@ - junit - junit - 3.8.1 - test + com.njzscloud + njzscloud-common-core + provided + + + com.jcraft + jsch + 0.1.55 diff --git a/njzscloud-common/njzscloud-common-sshtunnel/src/main/java/com/njzscloud/common/sshtunnel/SSHTunnel.java b/njzscloud-common/njzscloud-common-sshtunnel/src/main/java/com/njzscloud/common/sshtunnel/SSHTunnel.java new file mode 100644 index 0000000..33f2810 --- /dev/null +++ b/njzscloud-common/njzscloud-common-sshtunnel/src/main/java/com/njzscloud/common/sshtunnel/SSHTunnel.java @@ -0,0 +1,129 @@ +package com.njzscloud.common.sshtunnel; + +import cn.hutool.core.util.StrUtil; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.Session; +import com.njzscloud.common.core.tuple.Tuple2; +import lombok.extern.slf4j.Slf4j; + +import java.io.File; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +@Slf4j +public class SSHTunnel { + + private final List> tunnels = new LinkedList<>(); + private final ScheduledExecutorService reconnectExecutor = Executors.newSingleThreadScheduledExecutor(); + private final AtomicBoolean running = new AtomicBoolean(false); + + public SSHTunnel(SSHTunnelProperties sshTunnelProperties) { + if (sshTunnelProperties.isEnable()) { + log.info("SSH 隧道已启用"); + running.set(true); + List tunnelsProperties = sshTunnelProperties.getTunnels(); + tunnelsProperties.stream() + .filter(SSHTunnelProperties.SSHProperties::isEnable) + .forEach(it -> { + Tuple2 tunnel = createTunnel(it); + tunnels.add(tunnel); + }); + reconnectTask(); + } + } + + private Tuple2 createTunnel(SSHTunnelProperties.SSHProperties sshProperties) { + try { + String host = sshProperties.getHost(); + int port = sshProperties.getPort(); + String user = sshProperties.getUser(); + String pwd = sshProperties.getPwd(); + String credentials = sshProperties.getCredentials(); + String passphrase = sshProperties.getPassphrase(); + log.info("正在连接服务器:{}:{}", host, port); + JSch jsch = new JSch(); + Session session; + if (StrUtil.isNotBlank(credentials)) { + // 使用私钥认证 + File keyFile = new File(credentials); + if (!keyFile.exists()) { + throw new RuntimeException("私钥文件不存在: " + credentials); + } + + if (passphrase != null) { + jsch.addIdentity(credentials, passphrase); + } else { + jsch.addIdentity(credentials); + } + session = jsch.getSession(user, host, port); + } else if (StrUtil.isNotBlank(pwd)) { + // 创建SSH会话 + session = jsch.getSession(user, host, port); + // 使用密码认证 + session.setPassword(pwd); + } else { + throw new RuntimeException("未提供有效认证方式(私钥或密码)"); + } + + + session.setConfig("StrictHostKeyChecking", "no"); // 禁用主机密钥检查 + + // 设置保持活动状态 + session.setServerAliveInterval(60000); // 每分钟发送一次保持活动的数据包 + session.setServerAliveCountMax(3); // 允许3次失败 + + session.connect(); + + List proxyProperties = sshProperties.getProxy(); + for (SSHTunnelProperties.ProxyProperties proxyProperty : proxyProperties) { + int localPort = proxyProperty.getLocalPort(); + String targetHost = proxyProperty.getTargetHost(); + int targetPort = proxyProperty.getTargetPort(); + // 设置端口转发 (本地端口 -> 目标地址) + session.setPortForwardingL(localPort, targetHost, targetPort); + log.info("已创建隧道,本地端口:{} --> 服务器:{}:{} --> 目标地址:{}:{}", localPort, host, port, targetHost, targetPort); + } + return Tuple2.create(jsch, session); + } catch (JSchException e) { + log.error("SSH 隧道连接失败", e); + throw new RuntimeException("SSH 隧道连接失败", e); + } + } + + private void reconnectTask() { + reconnectExecutor.scheduleAtFixedRate(() -> { + if (!running.get()) return; + try { + for (Tuple2 tunnel : tunnels) { + Session session = tunnel.get_1(); + if (!session.isConnected()) { + log.warn("SSH 隧道连接已断开,尝试重新连接..."); + session.connect(); + } + } + } catch (Exception e) { + log.error("重连任务执行失败", e); + } + }, 30, 30, TimeUnit.SECONDS); // 每30秒检查一次连接状态 + } + + public void close() { + running.set(false); + reconnectExecutor.shutdownNow(); + for (Tuple2 tunnel : tunnels) { + Session session = tunnel.get_1(); + if (session != null && session.isConnected()) { + String host = session.getHost(); + int port = session.getPort(); + session.disconnect(); + log.info("SSH 隧道已关闭,服务器:{}:{}", host, port); + } + } + tunnels.clear(); + } +} diff --git a/njzscloud-common/njzscloud-common-sshtunnel/src/main/java/com/njzscloud/common/sshtunnel/SSHTunnelAutoConfiguration.java b/njzscloud-common/njzscloud-common-sshtunnel/src/main/java/com/njzscloud/common/sshtunnel/SSHTunnelAutoConfiguration.java new file mode 100644 index 0000000..e6d09a1 --- /dev/null +++ b/njzscloud-common/njzscloud-common-sshtunnel/src/main/java/com/njzscloud/common/sshtunnel/SSHTunnelAutoConfiguration.java @@ -0,0 +1,18 @@ +package com.njzscloud.common.sshtunnel; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration(proxyBeanMethods = false) +@ConditionalOnProperty(prefix = "ssh-tunnel", name = "enable", havingValue = "true") +@EnableConfigurationProperties({SSHTunnelProperties.class}) +public class SSHTunnelAutoConfiguration { + + @Bean(destroyMethod = "close") + public SSHTunnel sshTunnel(SSHTunnelProperties SSHTunnelProperties) { + return new SSHTunnel(SSHTunnelProperties); + } + +} diff --git a/njzscloud-common/njzscloud-common-sshtunnel/src/main/java/com/njzscloud/common/sshtunnel/SSHTunnelProperties.java b/njzscloud-common/njzscloud-common-sshtunnel/src/main/java/com/njzscloud/common/sshtunnel/SSHTunnelProperties.java new file mode 100644 index 0000000..a0000df --- /dev/null +++ b/njzscloud-common/njzscloud-common-sshtunnel/src/main/java/com/njzscloud/common/sshtunnel/SSHTunnelProperties.java @@ -0,0 +1,66 @@ +package com.njzscloud.common.sshtunnel; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; + +import java.util.List; + +@Getter +@Setter +@ConfigurationProperties("ssh-tunnel") +public class SSHTunnelProperties { + private boolean enable = false; + + private List tunnels; + + @Getter + @Setter + public static class SSHProperties { + private boolean enable = true; + + /** + * 服务器地址 + */ + private String host; + /** + * SSH 端口 + */ + private int port = 22; + /** + * 用户名 + */ + private String user; + /** + * 密码 + */ + private String pwd; + /** + * 秘钥文件 + */ + private String credentials; + /** + * 私钥密码 + */ + private String passphrase; + private List proxy; + } + + @Getter + @Setter + public static class ProxyProperties { + /** + * 本地端口 + */ + private int localPort; + /** + * 目标主机 + */ + private String targetHost; + /** + * 目标端口 + */ + private int targetPort; + } + +} diff --git a/njzscloud-common/njzscloud-common-sshtunnel/src/main/resources/META-INF/spring.factories b/njzscloud-common/njzscloud-common-sshtunnel/src/main/resources/META-INF/spring.factories new file mode 100644 index 0000000..e6f11d1 --- /dev/null +++ b/njzscloud-common/njzscloud-common-sshtunnel/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration = \ + com.njzscloud.common.sshtunnel.SSHTunnelAutoConfiguration diff --git a/njzscloud-svr/pom.xml b/njzscloud-svr/pom.xml index 65a682c..3cfe9fd 100644 --- a/njzscloud-svr/pom.xml +++ b/njzscloud-svr/pom.xml @@ -26,6 +26,10 @@ com.njzscloud njzscloud-common-http + + com.njzscloud + njzscloud-common-sshtunnel + com.njzscloud njzscloud-common-wechat diff --git a/njzscloud-svr/src/main/resources/application-dev.yml b/njzscloud-svr/src/main/resources/application-dev.yml index 6808a50..17d0708 100644 --- a/njzscloud-svr/src/main/resources/application-dev.yml +++ b/njzscloud-svr/src/main/resources/application-dev.yml @@ -1,11 +1,11 @@ spring: datasource: - url: jdbc:mysql://47.115.226.143:3306/njzscloud?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true&allowPublicKeyRetrieval=true&allowMultiQueries=true + # url: jdbc:mysql://47.115.226.143:3306/njzscloud?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true&allowPublicKeyRetrieval=true&allowMultiQueries=true + # username: root + # password: zsy2022 + url: jdbc:mysql://localhost:33061/njzscloud?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true&allowPublicKeyRetrieval=true&allowMultiQueries=true username: root - password: zsy2022 - # url: jdbc:mysql://localhost:33061/njzscloud?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true&allowPublicKeyRetrieval=true&allowMultiQueries=true - # username: root - # password: admin888999 + password: admin888999 security: auth-ignores: - /auth/obtain_code @@ -27,6 +27,7 @@ spring: - /payment/wechat/refundNotify - /district/areaList - /biz_audit_config/copy + - /order_info/gps_test app: default-place: province: 320000 @@ -47,18 +48,18 @@ oss: secret-key: zllX0ZJ1EwsZXT6dE6swCLgTF4ImGg bucket-name: cdn-zsy -mybatis-plus: - tunnel: - enable: false - ssh: +ssh-tunnel: + enable: true + tunnels: + - enable: true host: 139.224.54.144 port: 22 user: root credentials: D:/我的/再昇云/服务器秘钥/139.224.54.144_YZS_S1.pem - localPort: 33061 - db: - host: localhost - port: 33061 + proxy: + - localPort: 33061 + targetHost: localhost + targetPort: 33061 wechat: # app-id: wx3c06d9dd4e56c58d @@ -75,7 +76,7 @@ wechat: cert-serial-no: 1BCB1533688F349541C7B636EF67C666828BADBA # 文件路径 private-key-path: classpath:cert/apiclient_cert.p12 -# private-key-path: D:/project/再昇云/代码/njzscloud/njzscloud-svr/src/main/resources/cert/apiclient_cert.p12 + # private-key-path: D:/project/再昇云/代码/njzscloud/njzscloud-svr/src/main/resources/cert/apiclient_cert.p12 # 支付回调地址 notify-url: http://115.29.236.92:8082/payment/wechat/notify # 退款回调地址 diff --git a/pom.xml b/pom.xml index 8fcc7a6..6cebb7f 100644 --- a/pom.xml +++ b/pom.xml @@ -48,6 +48,11 @@ njzscloud-common-ws 0.0.1 + + com.njzscloud + njzscloud-common-sshtunnel + 0.0.1 + com.njzscloud njzscloud-common-sichen