最近找个springboot脚手架重新熟悉一下springboot相关框架的东西,结果发现好像项目还不能直接像数据库GUI工具一样填几个SSH参数就可以了,于是就给他再整一下看看如何解决

linux开放3306(可修改)端口直接访问

此方法较为方便,无需修改代码,改好后只要把springboot项目中的mysql访问地址改成对应的linux主机ip即可

在使用GUI数据库工具时我们可以发现刚刚安装了数据库使用ssh通道就可以直接连接了,连基本的配置权限过程都冇,这是因为我们通过SSH连接到linux服务器相当于是远程控制linux主机,并使用linux主机的ip进行数据库操作,并不是我们本机ip直接进行操作的,所以mysql服务器识别的还是localhost(等价于127.0.0.1),是默认配置放行的,并且官方其实也是推荐只留localhost的,不过此处就是提供一种方法供选择。

(1)首先开放mysql的非localhost访问权限,允许非本地ip进行访问
使用root(或者其他高权限用户)访问mysql库内的user表,将对应需要修改的开放对外访问的ip的用户中的host字段修改为%即可,表示通配所有ip进行放行操作。

另外如果准备开放其他端口而不是3306的话,需要到/etc/my.cnf处进行添加 port=***即可
修改完成后重启mysql数据库服务或者直接刷新权限

mysql -u root -dflush privileges;

systemctl restart mysqld

(2)开放linux防火墙
当我们有购买过云服务器的话,就可以发现我们需要在外部进行ip直接连接的话,需要在端口控制处添加对应的端口以供外部连接(当然也会配套一些DDOS防卫,流量控制一类的配套服务),在虚拟机的话也是同样需要开启对应的防火墙。
在网上搜寻了一大堆centos/rhel开放linux端口的文章,大部分都是让进行iptables配置,但是按照给出的文件路径,根本没有那个配置文件(/etc/sysconfig/iptables),我:?
后来才发现一位大佬的博客帮忙解了惑 https://www.cnblogs.com/AllWjw/p/15761697.html
总之就是每个centos/rhel大版本防火墙服务不尽相同,需要分开讨论,现在基本上主流Centos7所以使用的是firewalld服务

firewall-cmd --state ##查看防火墙状态,是否是runningfirewall-cmd --add-port=3306/tcp --permanent ##永久添加3306端口firewall-cmd --reload ##重新载入配置,比如添加规则之后,需要执行此命令firewall-cmd --list-ports ##查看当前开放的端口

按顺序执行下来即可,看起来对应的端口已经正常开放了。
先用navicat小试一下看看

测试直接连接成功,接下来走个springbootApplication直接连接(项目是直接去gitee拉下来的renren-security)

SpringBoot项目通过SSH通道进行数据库访问

这个的话应该很多人都能在网上搜到现成的代码,无非都是用jcraft这个工具包来进行远程转发,然后写一个连接类和监视类,修改一下配置文件即可,只是这样的话那我应该和上面的内容一样当天就写完这个博客了,这边主要是讲一下我踩得坑。

首先是对概念的认知,我以为的代码连接过程如下

实际上的运作方式

于是我的配置是这样的

String host = "192.168.23.129";//服务器公网IPint port = 22;// 跳板机ssh开放的接口 默认端口 22int local_port = 9680;// 这个是本地的端口,很重要!!!选取一个没有占用的port即可String remote_host = "127.0.0.1";// 要访问的mysql所在的host服务器局域网IP(127.0.0.1也行)int remote_port = 3306;// 服务器上数据库端口号

application.yml这边但是写了localhost:3306,实际上这里执行时还是会连本地的3306,没走刚刚定义的9680端口建立的SSH隧道

url: jdbc:mysql://localhost:3306/renren_security" />=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true

然后信心满满地启动,结果控制台每秒跳一个连接错

但是启动时还能瞟到有打印出SSH连接成功,在linux服务器查看连接也多出来了SSH连接,同时前端居然还能刷出来个验证码,可以证明这个地方应该程序是启动成功了来着,不过马上验证码就没了,因为开始报连接错误

看样子SSH是可以连接的没问题,报的错都是Druid连接池的错,难不成是因为创建连接池的时候也需要创建相同数量的SSH连接?于是路子渐渐地就走歪了…
我以为是因为我localport那个地方重复创建,所以只连接成功了一次后面全部都无法连接,原因是因为端口占用导致,于是开始魔改,增加Random类开始处理…

//int local_port = new Random(5000).nextInt();session.setPortForwardingL(new Random(5000).nextInt(), remote_host, remote_port);

结果依旧,SSH连接只走了一次。
隔了一天在网上寻求解决方法,又查了下Jcraft工具的运作方式

应当是由localhost:9680(本机开启端口)作为一个中转通道转发至目标服务器192.168.23.129:3306(我的linux虚拟机)才对,所以在application.yml中的url配置的显式是我们刚刚创建的一个本机的localhost:9680通道,实际上是在通过这个localhost:9680通过隧道代理到192.168.23.129:3306进行操作

所以,这个localport 应该是与application.yml这边的port是需要匹配的,因为这里的localhost:9680到linux服务器的SSH隧道就相当于代理至192.168.23.129:3306的转发者

url: jdbc:mysql://localhost:9680/renren_security" />=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=trueint local_port = 9680;

暂时在防火墙中将3306端口屏蔽,测试一下程序是否还能正常运行


程序通过建立SSH通道成功运行!

文章最后虽然这代码到处都有,姑且贴一下(示例项目是renren-security所以url那边配的数据库也是renren-security,如果要连接其他数据库再进行修改)
MyContextListener.java

package io.renren.commons.dynamic.datasource.ssh;import org.springframework.stereotype.Component;import javax.servlet.ServletContextEvent;import javax.servlet.ServletContextListener;import javax.servlet.annotation.WebListener;@WebListener@Componentpublic class MyContextListener implements ServletContextListener {private SSHConnection conexionssh;public MyContextListener() {super();}/** * //@see ServletContextListener#contextInitialized(ServletContextEvent) */@Overridepublic void contextInitialized(ServletContextEvent arg0) {// 建立连接System.out.println("Context initialized ... !\n\n\n");try {conexionssh = new SSHConnection();conexionssh.SSHConnection();System.out.println("\n\n\n成功建立SSH连接!\n\n\n");} catch (Throwable e) {System.out.println("\n\n\nSSH连接失败!\n\n\n");e.printStackTrace(); // error connecting SSH server}}/** * //@see ServletContextListener#contextDestroyed(ServletContextEvent) */@Overridepublic void contextDestroyed(ServletContextEvent arg0) {// 断开连接System.out.println("Context destroyed ... !\n\n\n");try {conexionssh.closeSSH(); // disconnectSystem.out.println("\n\n\n成功断开SSH连接!\n\n\n");} catch (Exception e) {e.printStackTrace();System.out.println("\n\n\n断开SSH连接出错!\n\n\n");}}}

SSHConnection.java

package io.renren.commons.dynamic.datasource.ssh;import com.jcraft.jsch.JSch;import com.jcraft.jsch.Session;import java.util.Random;public class SSHConnection {String user = "cloverangel";// 服务器登录名String password = "qq7744110";// 登陆密码String host = "192.168.23.129";//服务器公网IPint port = 22;// 跳板机ssh开放的接口 默认端口 22int local_port = 9680;// 这个是本地的端口,很重要!!!选取一个没有占用的port即可String remote_host = "127.0.0.1";// 要访问的mysql所在的host服务器局域网IP(127.0.0.1也行)int remote_port = 3306;// 服务器上数据库端口号Session session = null;/** *建立SSH连接 */public void SSHConnection() {try {JSch jsch = new JSch();session = jsch.getSession(user, host, port);session.setPassword(password);session.setConfig("StrictHostKeyChecking", "no");// 日志打印自己脑补session.connect();session.setPortForwardingL(local_port, remote_host, remote_port);System.out.println(local_port+"---"+remote_host+"---"+remote_port);} catch (Exception e) {e.printStackTrace();}}/** *断开SSH连接 */public void closeSSH () throws Exception{this.session.disconnect();}public int counter(Integer counter){System.out.println(counter);return counter+1;}}

application-dev.yml

url: jdbc:mysql://localhost:9680/renren_security" />=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true

pom.xml

<dependency><groupId>com.jcraft</groupId><artifactId>jsch</artifactId><version>0.1.55</version></dependency>