背景:
测试时本地需要连接测试环境的数据库,但测试环境与本地网络隔离不能直接访问,只能通过跳板机方式建立通道。
思路:
本地IP->跳板机IP->目标数据库
实现:
- 引入maven
JSch 是SSH2的一个纯Java实现。允许你连接到一个sshd 服务器,使用端口转发,X11转发,文件传输等等。可以将它的功能集成到你自己的程序中。同时该项目也提供一个J2ME版本用来在手机上直连SSHD服务器。
<!-- https://mvnrepository.com/artifact/com.jcraft/jsch -->
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.55</version>
</dependency>
- 配置数据库dataConfig.xml
<configuration>
<!-- 注册对象的空间命名 -->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!-- 1.加载数据库驱动 -->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<!-- 2.数据库连接地址 -->
<property name="url" value="jdbc:mysql://127.0.0.1:目标机端口号"/>
<!-- 数据库用户... -->
<property name="username" value="用户名"/>
<!-- 数据库密码... -->
<property name="password" value="密码"/>
</dataSource>
</environment>
</environments>
<!-- 注册映射文件:java对象与数据库之间的xml文件路径! -->
<mappers>
<mapper resource="mapper/SQLMapper.xml"/>
</mappers>
</configuration>
- 创建/关闭SSH通道
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
class SshChannel {
private Session session;
private Channel channel;
/**
*
* @param localPort 本地触发端口,配置文件中数据库端口与目标机端口一致
* @param sshHost ssh host
* @param sshPort ssh port
* @param sshUserName ssh用户名
* @param sshPassWord ssh密码
* @param targetHost 目标机地址
* @param targetPort 目标端口
*/
public void gotoSSH(int localPort, String sshHost, int sshPort,
String sshUserName, String sshPassWord,
String targetHost, int targetPort) {
try {
JSch jsch = new JSch();
//登陆跳板机
session = jsch.getSession(sshUserName, sshHost, sshPort);
session.setPassword(sshPassWord);
session.setConfig("StrictHostKeyChecking", "no");
session.connect();
//建立通道
channel = session.openChannel("session");
channel.connect();
//通过ssh连接到目标数据库
int assinged_port = session.setPortForwardingL(localPort, targetHost, targetPort);
System.out.println("通道建立成功~!");
} catch (JSchException e) {
throw new RuntimeException("无法使用ssh登陆,请检查用户名密码或端口" + e);
}
}
/**
* 关闭通道
*/
public void close() {
if (session != null && session.isConnected() ) {
session.disconnect();
}
if (channel != null && session.isConnected() ) {
channel.disconnect();
}
}
}
- 连接数据库
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.Reader;
public class DatabaseUtil {
private static ThreadLocal<SqlSession> threadLocal = new ThreadLocal<SqlSession>();
private static ThreadLocal<SSHChannel> channelThreadLocal = new ThreadLocal<SSHChannel>();
/**
* 禁止外界通过new方法创建
*/
private DatabaseUtil() {
}
public static SqlSession getSqlSession() throws IOException {
/**
* 加载MybatisConfig.xml配置文件
* 配置文件中的连接数据库的地址需要改成“127.0.0.1”,端口需要改成“本地映射端口(目标端口)”
*/
Reader reader = Resources.getResourceAsReader("databaseConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader);
SshChannel sshChannel = channelThreadLocal.get();
if (sshChannel == null){
sshChannel = new SSHChannel();
//建立一个通道,将本地端口通过跳板机映射到远程服务器的指定端口
sshChannel.gotoSSH(本地映射端口, "跳板机sshIP",
跳板机port, "跳板机用户名", "跳板机密码",
"目标IP", 目标端口);
channelThreadLocal.set(sshChannel);
}
SqlSession sqlSession = threadLocal.get();
if (sqlSession == null){
sqlSession = factory.openSession();
threadLocal.set(sqlSession);
}
return sqlSession;
}
/**
* 关闭SqlSession和sshChannel连接
*/
public static void closeSqlSession() {
SqlSession sqlSession = threadLocal.get();
if (sqlSession != null) {
sqlSession.close();
threadLocal.remove();
}
SSHChannel sshChannel = channelThreadLocal.get();
if (sshChannel != null) {
sshChannel.close();
channelThreadLocal.remove();
}
}
}
如果输出下图内容,则连接成功。