以下列举了程序使用 ssh 到跳板机,通过跳板机连接 mysql 数据库的过程。
首先是 maven 依赖
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.53</version>
</dependency>
看了下基本都是用这个包,但是这里有一个问题,就是现在通过 ssh-keygen 生成的私钥,都是无法通过这个 jar 包使用的。此时需要将本地的私钥换成经典的加密方式。打开本地的 id_rsa 文件,如头部为以下文本,则需要执行命令 ssh-keygen -p -f id_rsa -m pem -P "" -N "" (id_rsa 为 id_rsa 文件的路径)
-----BEGIN OPENSSH PRIVATE KEY-----
执行完后,id_rsa 文件的第一行为
-----BEGIN RSA PRIVATE KEY-----
然后是程序,不细说了,该封装的已经封装好,有不明白的可以留言。
@Slf4j
@WebListener
@Component
public class MyContextListener implements ServletContextListener {
@Autowired
private SSHDataBaseConfig dataBaseConfig;
/**
* 监听Servlet初始化事件
*/
@Override
public void contextInitialized(ServletContextEvent arg0) {
// 建立连接
try {
dataBaseConfig.createSSH();
} catch (Throwable e) {
e.printStackTrace(); // error connecting SSH server
}
}
/**
* 监听Servlet终止事件
*/
@Override
public void contextDestroyed(ServletContextEvent arg0) {
// 断开连接
try {
dataBaseConfig.closeSSH(); // disconnect
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Component
@EnableConfigurationProperties(SSHProperties.class)
@Slf4j
public class SSHDataBaseConfig implements ServletContextListener {
@Autowired
private SSHProperties sshConfig;
//represents each ssh session
private Session session;
public void createSSH() {
if (sshConfig == null || sshConfig.getEnabled == null || !sshConfig.getEnabled()) {
return;
}
try {
log.info("ssh forward is opend.");
log.info("ssh init ……");
JSch sch = new JSch();
if (null != sshConfig.getSshKnownHosts() && !"".equals(sshConfig.getSshKnownHosts())) {
sch.setKnownHosts(sshConfig.getSshKnownHosts());
}
if (null != sshConfig.getSshKeyPath() && !"".equals(sshConfig.getSshKeyPath())) {
sch.addIdentity(sshConfig.getSshKeyPath());
}
session = sch.getSession(sshConfig.getUsername(), sshConfig.getHost(), sshConfig.getPort());
session.setConfig("StrictHostKeyChecking", "no");
if (null != sshConfig.getPassword() && !"".equals(sshConfig.getPassword())) {
session.setPassword(sshConfig.getPassword());
}
session.connect();
session.setPortForwardingL(sshConfig.getFromHost(),sshConfig.getFromPort(), sshConfig.getToHost(), sshConfig.getToPort());
log.info("ssh init sucessful……");
} catch (JSchException e) {
log.error("ssh JSchException failed.", e);
} catch (Exception e) {
log.error("ssh settings is failed. skip!", e);
}
}
/**
* 关闭SSH连接
*/
public void closeSSH() {
if (sshConfig == null || sshConfig.getEnabled == null || !sshConfig.getEnabled()) {
return;
}
session.disconnect();
}
}
@ConfigurationProperties(prefix = "spring.datasource.ssh.forward")
@Data
public class SSHProperties {
private Boolean enabled;
private String sshKeyPath;
private String sshKnownHosts;
private String host;
private Integer port = -1;
private String username;
private String password;
private String fromHost;
private Integer fromPort = -1;
private String toHost;
private Integer toPort = -1;
}
spring:
datasource:
ssh:
forward:
enabled: true
ssh-key-path: ~/.ssh/id_rsa
ssh-known-hosts:
host: 跳板机IP
port: 跳板机SSH端口
username: 跳板机SSH用户名
password: 跳板机SSH密码(没有不填)
from-host: 127.0.0.1(就填本地就行,mysql连接那里就写这个)
from-port: 3307(随便写,不冲突就行,mysql连接那里就写这个)
to-host: mysql服务器真实IP
to-port: mysql服务器真实端口
druid:
datasource:
master:
url: jdbc:mysql://127.0.0.1:3307/xxxx?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
username: xxx
password: xxx
driver-class-name: com.mysql.cj.jdbc.Driver
-------------------20211230-------------------------
ssh 是有命令做到这种遂道的,如下
ssh -f root@{host} -p 22 -L {from-port}:{from-host}:{to-port} -N
以上示例,
假设mysql服务器在云上一台没开外网的服务器上,假设这台服务器IP为 10.0.0.0,数据库端口3306
假设现在有一台跳板机开了ssh权限,假设该服务器IP为 20.0.0.0,ssh端口为 22
需要连接数据库的这台服务器,只有跳板机的ssh权限,假设这台服务器为 30.0.0.0,此时应如何在这台机器连接数据库?
在 30.0.0.0 这台服务器执行命令 ssh -f root@20.0.0.0 -p 22 -L 9999:127.0.0.1:3306
此时30.0.0.0这台服务器,可以使用 127.0.0.1:9999 这个连接访问数据库。
参考链接
springboot 使用SSH 通过A服务器跳板机 连接B服务器Mysql(安全策略)_李虹柏的博客-CSDN博客_springboot ssh连接服务器
java - "Invalid privatekey" when using JSch - Stack Overflow