Java通过SSH+MyBatis连接远端数据库

949 阅读2分钟

背景:

测试时本地需要连接测试环境的数据库,但测试环境与本地网络隔离不能直接访问,只能通过跳板机方式建立通道。

思路:

本地IP->跳板机IP->目标数据库

实现:

  1. 引入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>
  1. 配置数据库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>
  1. 创建/关闭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();
        }
    }
}
  1. 连接数据库
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();
        }

    }
}

如果输出下图内容,则连接成功。

image.png