Spring 带有到远程 MySQL 服务器的 ssh 隧道的数据 JPA
Spring Data JPA with ssh tunnel to a remote MySQL server
我正在使用 Spring Data JPA 和 Hibernate 作为持久性提供程序,并与远程 MySQL5 服务器一起用于定期复制内部数据子集的作业。该作业(即 quartz-scheduled java 应用程序)每天运行一次,需要大约。 30秒完成同步)。出于安全原因,我们不想为来自外部的直接连接(即本地主机以外的连接)打开远程服务器。
我看过使用 Jsch 以编程方式设置 ssh 隧道的示例,但找不到有关如何将 Jsch 与 spring 数据集成的任何资源。我看到的一个问题是我的某些 spring bean(即 org.apache.commons.configuration.DatabaseConfiguration)是在应用程序启动时创建的,并且已经需要访问数据源。
我可以在应用程序外部打开 ssh 隧道,但它会一直打开,但我想避免这种情况,因为我只需要每天打开 30 秒。
编辑:
经过一些研究,我找到了几种获取 ssh 隧道的方法
A) 实现我自己的数据源(我扩展了 org.springframework.jdbc.datasource.DriverManagerDataSource),然后使用 PostContruct 和 Predestroy 来设置/关闭带有 Jsch 的 ssh 隧道
--> 问题:ssh 隧道在应用程序的生命周期内保持打开状态,这不是我想要的
B) 实现我自己的驱动程序(我扩展了 com.mysql.jdbc.Driver)并覆盖 "connect" 以在连接前创建 ssh 隧道
--> 问题:我无法关闭 ssh 隧道连接
欢迎提出更多建议
如果您的 Spring 配置中有一个 DataSource
bean,您可以创建自己的 DataSource
实现,在尝试使用提供的 JDBC URL。例如,考虑以下使用 HikariDataSource
的配置:
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource">
<bean class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">...</bean>
</property>
</bean>
您可以扩展 class HikariDataSource
以提供您自己的实现。下面是一个例子:
class TunneledHikariDataSource extends HikariDataSource implements InitializingBean {
private boolean createTunnel = true;
private int tunnelPort = 3306;
public void afterPropertiesSet() {
if(createTunnel) {
// 1. Extract remote host name from the JDBC URL.
// 2. Extract/infer remote tunnel port (e.g. 3306)
// from the JDBC URL.
// 3. Create a tunnel using Jsch and sample code
// at http://www.jcraft.com/jsch/examples/PortForwardingL.java.html
...
}
}
}
然后,为自定义实例化一个 bean 实例 class 而不是 HikariDataSource
。
在这里扩展@manish 的回答是我的解决方案,适用于 Spring 2022 年启动:
@Configuration
class DataSourceInitializer {
@Bean
fun dataSource(properties: DataSourceProperties): DataSource {
return properties.initializeDataSourceBuilder().type(SshTunnelingHikariDataSource::class.java).build()
}
}
class SshTunnelingHikariDataSource : HikariDataSource(), InitializingBean {
override fun afterPropertiesSet() {
val jsch = JSch()
val filePath = javaClass.classLoader.getResource("id_rsa")?.toURI()?.path
jsch.addIdentity(filePath, "optional_key_file_passphrase")
val session = jsch.getSession("root", "remote-host.com")
val config = Properties()
config["StrictHostKeyChecking"] = "no";
session.setConfig(config)
session.connect()
session.setPortForwardingL(3307, "db-host.com", 3306)
}
}
根据您的连接,您可能不需要身份文件(即私钥),但您可能需要用户名 + 密码,您可以在 getSession
.
提供
remote-host.com
是您希望终止 SSH 会话的主机,可以访问数据库
db-host.com
是您的 remote-host 可以解析的主机。如果数据库在您的 remote-host
本地 运行,它也可能是 localhost
我正在使用 Spring Data JPA 和 Hibernate 作为持久性提供程序,并与远程 MySQL5 服务器一起用于定期复制内部数据子集的作业。该作业(即 quartz-scheduled java 应用程序)每天运行一次,需要大约。 30秒完成同步)。出于安全原因,我们不想为来自外部的直接连接(即本地主机以外的连接)打开远程服务器。
我看过使用 Jsch 以编程方式设置 ssh 隧道的示例,但找不到有关如何将 Jsch 与 spring 数据集成的任何资源。我看到的一个问题是我的某些 spring bean(即 org.apache.commons.configuration.DatabaseConfiguration)是在应用程序启动时创建的,并且已经需要访问数据源。
我可以在应用程序外部打开 ssh 隧道,但它会一直打开,但我想避免这种情况,因为我只需要每天打开 30 秒。
编辑:
经过一些研究,我找到了几种获取 ssh 隧道的方法
A) 实现我自己的数据源(我扩展了 org.springframework.jdbc.datasource.DriverManagerDataSource),然后使用 PostContruct 和 Predestroy 来设置/关闭带有 Jsch 的 ssh 隧道
--> 问题:ssh 隧道在应用程序的生命周期内保持打开状态,这不是我想要的
B) 实现我自己的驱动程序(我扩展了 com.mysql.jdbc.Driver)并覆盖 "connect" 以在连接前创建 ssh 隧道
--> 问题:我无法关闭 ssh 隧道连接
欢迎提出更多建议
如果您的 Spring 配置中有一个 DataSource
bean,您可以创建自己的 DataSource
实现,在尝试使用提供的 JDBC URL。例如,考虑以下使用 HikariDataSource
的配置:
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource">
<bean class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">...</bean>
</property>
</bean>
您可以扩展 class HikariDataSource
以提供您自己的实现。下面是一个例子:
class TunneledHikariDataSource extends HikariDataSource implements InitializingBean {
private boolean createTunnel = true;
private int tunnelPort = 3306;
public void afterPropertiesSet() {
if(createTunnel) {
// 1. Extract remote host name from the JDBC URL.
// 2. Extract/infer remote tunnel port (e.g. 3306)
// from the JDBC URL.
// 3. Create a tunnel using Jsch and sample code
// at http://www.jcraft.com/jsch/examples/PortForwardingL.java.html
...
}
}
}
然后,为自定义实例化一个 bean 实例 class 而不是 HikariDataSource
。
在这里扩展@manish 的回答是我的解决方案,适用于 Spring 2022 年启动:
@Configuration
class DataSourceInitializer {
@Bean
fun dataSource(properties: DataSourceProperties): DataSource {
return properties.initializeDataSourceBuilder().type(SshTunnelingHikariDataSource::class.java).build()
}
}
class SshTunnelingHikariDataSource : HikariDataSource(), InitializingBean {
override fun afterPropertiesSet() {
val jsch = JSch()
val filePath = javaClass.classLoader.getResource("id_rsa")?.toURI()?.path
jsch.addIdentity(filePath, "optional_key_file_passphrase")
val session = jsch.getSession("root", "remote-host.com")
val config = Properties()
config["StrictHostKeyChecking"] = "no";
session.setConfig(config)
session.connect()
session.setPortForwardingL(3307, "db-host.com", 3306)
}
}
根据您的连接,您可能不需要身份文件(即私钥),但您可能需要用户名 + 密码,您可以在 getSession
.
remote-host.com
是您希望终止 SSH 会话的主机,可以访问数据库
db-host.com
是您的 remote-host 可以解析的主机。如果数据库在您的 remote-host