springboot 通过 ssh 连接远程数据库mysql

3 篇文章 0 订阅
3 篇文章 0 订阅

前因:处于安全考虑,运维老哥禁止了数据库远程访问,导致我们不能通过本地直连数据库了。

 但运维老哥还告诉我们,还是可以通过ssh网络协议做跳板连接,所以就有了以下尝试:

首先现在Navicat 这个工具上尝试了ssh连接能不能通。

ok,确保我们的ssh账号,密码没问题。接下来进行代码层面的ssh连接。

对ssh不太了解的,可以看下我上一篇文章,方便理解下面的代码操作。
下面操作都是基于windows上连接远程linux服务器的操作。

进入代码层面(基于springboot+mybatis框架结构)。
 

1、首先maven中导入必须的 jsch 包。

        <!--SSH 连接-->
        <dependency>
            <groupId>com.jcraft</groupId>
            <artifactId>jsch</artifactId>
            <version>0.1.55</version>
        </dependency>

2、编写一个ssh连接的工具类。

这里你得先理清自己想要通过什么方式连接?
①、通过账号密码方式进行ssh桥接。
②、通过公私钥方式进行ssh桥接。

第一种方式(账号密码):

package com.xcj.juhe.tool;

import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;

import java.util.Properties;

/**
 * @author xcj
 * @date 2021/7/14 10:26
 */
public class SshConnectionTool {

    //ssh连接的用户名
    private final static String SSH_USER = "root";
    //ssh连接的密码
    private final static String SSH_PASSWORD = "123456";
    //ssh远程连接的ip地址
    private final static String SSH_REMOTE_SERVER = "42.192.77.88";
    //ssh连接的端口号
    private final static int SSH_REMOTE_PORT = 36000;
    //本地mysql发起连接的IP地址
    private final static String MYSQL_REMOTE_SERVER = "127.0.0.1";
    //本地数据库连接时用的端口号
    private final static int LOCAl_PORT = 3307;
    //远程数据库端口用的端口号
    private final static int REMOTE_PORT = 3306;

    private Session sesion; //ssh 会话

    public void closeSSH ()
    {
        sesion.disconnect();
    }

    public SshConnectionTool () throws Throwable
    {

        JSch jsch = new JSch();

        sesion = jsch.getSession(SSH_USER, SSH_REMOTE_SERVER, SSH_REMOTE_PORT);

        sesion.setPassword(SSH_PASSWORD);

        //设置连接过程不校验known_hosts文件中的信息
        Properties config = new Properties();
        config.put("StrictHostKeyChecking", "no");
        sesion.setConfig(config);

        sesion.connect(); //ssh 建立连接!

        //根据安全策略,您必须通过转发端口进行连接
        sesion.setPortForwardingL(LOCAl_PORT, MYSQL_REMOTE_SERVER, REMOTE_PORT);

    }


}

第二种方式(公私钥)(提前将自己生成的公钥搞到服务器上):

package com.xcj.juhe.tool;

import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;

import java.util.Properties;

/**
 * @author xcj
 * @date 2021/7/14 10:26
 */
public class SshConnectionTool {
    //本地的ssh中的knownhost文件路径
    private final static String S_PATH_FILE_KNOWN_HOSTS = "C:/Users/lx/.ssh/known_hosts";
    //本地的ssh密钥路径
    private final static String S_PATH_FILE_PRIVATE_KEY = "C:/Users/lx/.ssh/id_rsa";

    //ssh连接的用户名
    private final static String SSH_USER = "root";
    //ssh远程连接的ip地址
    private final static String SSH_REMOTE_SERVER = "42.192.77.88";
    //ssh连接的端口号
    private final static int SSH_REMOTE_PORT = 36000;
    //本地mysql发起连接的IP地址
    private final static String MYSQL_REMOTE_SERVER = "127.0.0.1";
    //本地数据库连接时用的端口号
    private final static int LOCAl_PORT = 3307;
    //远程数据库端口用的端口号
    private final static int REMOTE_PORT = 3306;

    private Session sesion; //ssh 会话

    public void closeSSH ()
    {
        sesion.disconnect();
    }

    public SshConnectionTool () throws Throwable
    {

        JSch jsch = null;

        jsch = new JSch();
        //设置known_hosts文件路径,如:~/.ssh/known_hosts(known_hosts中存储是已认证的远程主机host key)
        jsch.setKnownHosts(S_PATH_FILE_KNOWN_HOSTS);
        //设置私钥
        jsch.addIdentity(S_PATH_FILE_PRIVATE_KEY);

        sesion = jsch.getSession(SSH_USER, SSH_REMOTE_SERVER, SSH_REMOTE_PORT);

        //设置连接过程不校验known_hosts文件中的信息
        Properties config = new Properties();
        config.put("StrictHostKeyChecking", "no");
        sesion.setConfig(config);

        sesion.connect(); //ssh 建立连接!

        //根据安全策略,您必须通过转发端口进行连接
        sesion.setPortForwardingL(LOCAl_PORT, MYSQL_REMOTE_SERVER, REMOTE_PORT);

    }


}

3、编写监听器(监听ServletContext 对象的生命周期,用于ssh桥接)

package com.xcj.juhe.listener;

import com.xcj.juhe.tool.SshConnectionTool;
import org.springframework.stereotype.Component;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;

/**
 * @author xcj
 * @date 2021/7/14 10:36
 */
@Component
public class SshContextListener implements ServletContextListener {
    private SshConnectionTool conexionssh;
    public SshContextListener() {
        super();
    }
    /**
     * @see ServletContextListener#contextInitialized(ServletContextEvent)
     */
    public void contextInitialized(ServletContextEvent arg0) {
        System.out.println("Context initialized ... !");
        try {
            conexionssh = new SshConnectionTool();
        } catch (Throwable e) {
            e.printStackTrace(); // 连接失败
        }
    }

    /**
     * @see ServletContextListener#contextDestroyed(ServletContextEvent)
     */
    public void contextDestroyed(ServletContextEvent arg0) {
        System.out.println("Context destroyed ... !");
        conexionssh.closeSSH(); // 断开连接
    }
}

4、编写yml 数据库连接的配置文件

spring:
  datasource:
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://127.0.0.1:3307/mint_sdk?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
        username: root
        password: 123456

5、你自己写个controller去进行数据库操作,试下能不能成功。

6、调试期间,我遇到的异常和排雷操作:

①、通过公私钥方式连接,出现公私钥不匹配时会报这个异常(我自己改了私钥,导致私钥过长)。

com.jcraft.jsch.JSchException: fromBase64: invalid base64 data

 解决方法:
找到ssh 的 know_hosts文件,删除对应的远程服务器连接信息。

 问题解决。

②、ssh工具类中,没有加忽略对比known_hosts文件内容,导致每次内容变更就连接失败。

  解决方法:

1)、如上述工具类代码中添加忽略对比代码:

        //设置连接过程不校验known_hosts文件中的信息
        Properties config = new Properties();
        config.put("StrictHostKeyChecking", "no");
        sesion.setConfig(config);

2)、删除known_hosts文件中对应的连接主机的信息。

-------------------------------------------------------------------------------

为什么需要known_hosts?

最后探讨下为什么需要known_hosts,这个文件主要是通过Client和Server的双向认证,从而避免中间人(man-in-the-middle attack)攻击,每次Client向Server发起连接的时候,不仅仅Server要验证Client的合法性,Client同样也需要验证Server的身份,SSH client就是通过known_hosts中的host key来验证Server的身份的。
用OpenSSH的人都知ssh会把你每个你访问过计算机的公钥(public key)都记录在~/.ssh/known_hosts。当下次访问相同计算机时,OpenSSH会核对公钥。如果公钥不同,OpenSSH会发出警告。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值