使用 Java 通过 SSH 连接到远程 MySQL 数据库
Posted
技术标签:
【中文标题】使用 Java 通过 SSH 连接到远程 MySQL 数据库【英文标题】:Connect to remote MySQL database through SSH using Java 【发布时间】:2010-12-30 09:51:42 【问题描述】:如何从 java 应用程序通过 SSH 连接到远程 mysql 数据库?小代码示例对我很有帮助,我将不胜感激。
【问题讨论】:
看看this answer... 【参考方案1】:虽然现有答案是正确的,但它们掩盖了其他代码膨胀中的重要代码。
这是通过 SSH 通道建立 JDBC(或任何其他)数据库连接所需的基本代码:
String jumpserverHost = "ssh.example.com";
String jumpserverUsername = "sshuser";
// The hostname/IP address and port, you would use on the SSH server
// to connect to the database.
// If the database runs on the same machine as the SSH server, use "localhost".
String databaseHost = "database.example.com";
int databasePort = 3306;
String databaseUsername = "dbuser";
String databasePassword = "dbpass";
JSch jsch = new JSch();
// Public key authentication example
// (but you can use password authentication, if appropriate).
jsch.addIdentity("~/.ssh/id_rsa");
// Connect to SSH jump server (this does not show an authentication code)
Session session = jsch.getSession(jumpserverUsername, jumpserverHost);
session.connect();
// Forward randomly chosen local port through the SSH channel to database host/port
int forwardedPort = session.setPortForwardingL(0, databaseHost, databasePort);
// Connect to the forwarded port (the local end of the SSH tunnel)
// If you don't use JDBC, but another database client,
// just connect it to the localhost:forwardedPort
String url = "jdbc:mysql://localhost:" + forwardedPort;
Connection con =
DriverManager.getConnection(url, databaseUsername, databasePassword);
您还必须处理主机密钥验证。为此,请参阅:How to resolve Java UnknownHostKey, while using JSch SFTP library?
【讨论】:
【参考方案2】:首先,谢谢你的作品很棒!
不过,我想知道是否应该为每个(可能同时的)SQL 连接重用该 Session,或者我是否应该每次都创建一个新 Session,并且仅在由于某种原因过期时才刷新它。
目前,每次建立连接时,我都会在此处创建该控制器的新实例,然后使用从中获得的连接执行 SQL 查询,然后手动关闭它。
如果我可以使该类与 try-with-resource 一起使用并且它会自行关闭,那也很好。会调查的。因为我不想错过关闭它。
事情是这样的,我现在正在获取数据库连接。
public class ConnectionManager
private Connection con = null;
private Session session = null;
public Connection getConnection()
Connection con = null;
var settings = new DbSettingsController();
boolean useSSH = settings.getSetting(SettingKey.UseSSH).equals("true");
String sshPort = settings.getSetting(SettingKey.SSHPort);
String sqlIp = settings.getSetting(SettingKey.MySqlIP);
String sqlPort = settings.getSetting(SettingKey.MySqlPort);
if(useSSH)
JSch jSch = new JSch();
try
this.session = jSch.getSession(settings.getSetting(SettingKey.SSHUser),
settings.getSetting(SettingKey.SSHHost),
Integer.valueOf(sshPort));
this.session.setPassword(settings.getSetting(SettingKey.SSHPassword));
this.session.setConfig("StrictHostKeyChecking", "no");
this.session.connect();
this.session.setPortForwardingL(Integer.parseInt(sshPort), sqlIp, Integer.parseInt(sqlPort));
catch (JSchException e)
e.printStackTrace();
var connectionString = String.format("jdbc:mysql://%s:%s/%s?autoReconnect=true&useSSL=false",
sqlIp, useSSH ? sshPort : sqlPort,
settings.getSetting(SettingKey.MySqlShema));
var user = settings.getSetting(SettingKey.MySqlUser);
var password = settings.getSetting(SettingKey.MySqlPassword);
try
con = DriverManager.getConnection(connectionString, user, password);
catch (SQLException e)
e.printStackTrace();
return con;
public void close()
if(this.con != null)
try
this.con.close();
catch (SQLException e)
e.printStackTrace();
if(this.session != null)
this.session.disconnect();
如果您想知道我自己也制作了 DbSettingsController,只需将设置放在本地 SQLite DB 的 Text 列中,并为其分配一个键(即 enum 的 int 值)。只是复制粘贴我从其他项目中重复使用的代码,所以这样做既简单又快速。
【讨论】:
您实际上将必须重用会话,否则多个连接将失败。现在这不是一个真正的问题,因为我有 1 个执行程序运行一个 1 个线程池,我将所有选择放入,为视图提供数据。但是我稍后会在此基础上进行更新,所以这会失败,因为它们可能(并不总是)与选择重叠。所以,无论如何,我仍然将会话更改为静态,并在关闭应用程序时调用 shutdown,我们将看看是否有任何问题。【参考方案3】:import java.sql.Connection;
import java.sql.DriverManager;
import java.util.Properties;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
public class CTestDriver
private static void doSshTunnel(String strSshUser, String strSshPassword, String strSshHost, int nSshPort,
String strRemoteHost, int nLocalPort, int nRemotePort) throws JSchException
final JSch jsch = new JSch();
Session session = jsch.getSession(strSshUser, strSshHost, 22);
session.setPassword(strSshPassword);
final Properties config = new Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
session.connect();
session.setPortForwardingL(nLocalPort, strRemoteHost, nRemotePort);
public static void main(String[] args)
try
String strSshUser = "ssh_user_name"; // SSH loging username
String strSshPassword = "abcd1234"; // SSH login password
String strSshHost = "your.ssh.hostname.com"; // hostname or ip or
// SSH server
int nSshPort = 22; // remote SSH host port number
String strRemoteHost = "your.database.hostname.com"; // hostname or
// ip of
// your
// database
// server
int nLocalPort = 3366; // local port number use to bind SSH tunnel
int nRemotePort = 3306; // remote port number of your database
String strDbUser = "db_user_name"; // database loging username
String strDbPassword = "4321dcba"; // database login password
CTestDriver.doSshTunnel(strSshUser, strSshPassword, strSshHost, nSshPort, strRemoteHost, nLocalPort,
nRemotePort);
Class.forName("com.mysql.jdbc.Driver");
Connection con = DriverManager.getConnection("jdbc:mysql://localhost:" + nLocalPort, strDbUser,
strDbPassword);
con.close();
catch (Exception e)
e.printStackTrace();
finally
System.exit(0);
【讨论】:
给出解决方案的一些细节。 可以说,当您想使用另一台主机连接到远程主机时,即连接到一台主机并从那里连接到主主机。这样做是出于安全目的。 【参考方案4】:package framework.restapi.utils;
import java.sql.*;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.List;
public class SQLConnection
private static Connection connection = null;
private static Session session = null;
private static void connectToServer(String dataBaseName) throws SQLException
connectSSH();
connectToDataBase(dataBaseName);
private static void connectSSH() throws SQLException
String sshHost = "";
String sshuser = "";
String dbuserName = "";
String dbpassword = "";
String SshKeyFilepath = "/Users/XXXXXX/.ssh/id_rsa";
int localPort = 8740; // any free port can be used
String remoteHost = "127.0.0.1";
int remotePort = 3306;
String localSSHUrl = "localhost";
/***************/
String driverName = "com.mysql.jdbc.Driver";
try
java.util.Properties config = new java.util.Properties();
JSch jsch = new JSch();
session = jsch.getSession(sshuser, sshHost, 22);
jsch.addIdentity(SshKeyFilepath);
config.put("StrictHostKeyChecking", "no");
config.put("ConnectionAttempts", "3");
session.setConfig(config);
session.connect();
System.out.println("SSH Connected");
Class.forName(driverName).newInstance();
int assinged_port = session.setPortForwardingL(localPort, remoteHost, remotePort);
System.out.println("localhost:" + assinged_port + " -> " + remoteHost + ":" + remotePort);
System.out.println("Port Forwarded");
catch (Exception e)
e.printStackTrace();
private static void connectToDataBase(String dataBaseName) throws SQLException
String dbuserName = "sf2_showpad_biz";
String dbpassword = "lOAWEnL3K";
int localPort = 8740; // any free port can be used
String localSSHUrl = "localhost";
try
//mysql database connectivity
MysqlDataSource dataSource = new MysqlDataSource();
dataSource.setServerName(localSSHUrl);
dataSource.setPortNumber(localPort);
dataSource.setUser(dbuserName);
dataSource.setAllowMultiQueries(true);
dataSource.setPassword(dbpassword);
dataSource.setDatabaseName(dataBaseName);
connection = dataSource.getConnection();
System.out.print("Connection to server successful!:" + connection + "\n\n");
catch (Exception e)
e.printStackTrace();
private static void closeConnections()
CloseDataBaseConnection();
CloseSSHConnection();
private static void CloseDataBaseConnection()
try
if (connection != null && !connection.isClosed())
System.out.println("Closing Database Connection");
connection.close();
catch (SQLException e)
e.printStackTrace();
private static void CloseSSHConnection()
if (session != null && session.isConnected())
System.out.println("Closing SSH Connection");
session.disconnect();
// works ONLY FOR single query (one SELECT or one DELETE etc)
private static ResultSet executeMyQuery(String query, String dataBaseName)
ResultSet resultSet = null;
try
connectToServer(dataBaseName);
Statement stmt = connection.createStatement();
resultSet = stmt.executeQuery(query);
System.out.println("Database connection success");
catch (SQLException e)
e.printStackTrace();
return resultSet;
public static void DeleteOrganisationReferencesFromDB(String organisationsLike)
try
connectToServer("ServerName");
Statement stmt = connection.createStatement();
ResultSet resultSet = stmt.executeQuery("select * from DB1");
String organisationsToDelete = "";
List<String> organisationsIds = new ArrayList<String>();
// create string with id`s values to delete organisations references
while (resultSet.next())
String actualValue = resultSet.getString("id");
organisationsIds.add(actualValue);
for (int i = 0; i < organisationsIds.size(); i++)
organisationsToDelete = " " + organisationsToDelete + organisationsIds.get(i);
if (i != organisationsIds.size() - 1)
organisationsToDelete = organisationsToDelete + ", ";
stmt.executeUpdate(" DELETE FROM `DB1`.`table1` WHERE `DB1`.`table1`.`organisation_id` in ( " + organisationsToDelete + " );");
catch (SQLException e)
e.printStackTrace();
finally
closeConnections();
public static List<String> getOrganisationsDBNamesBySubdomain(String organisationsLike)
List<String> organisationDbNames = new ArrayList<String>();
ResultSet resultSet = executeMyQuery("select `DB`.organisation.dbname from `DB1`.organisation where subdomain like '" + organisationsLike + "%'", "DB1");
try
while (resultSet.next())
String actualValue = resultSet.getString("dbname");
organisationDbNames.add(actualValue);
catch (SQLException e)
e.printStackTrace();
finally
closeConnections();
return organisationDbNames;
public static List<String> getAllDBNames()
// get all live db names incentral DB
List<String> organisationDbNames = new ArrayList<String>();
ResultSet resultSet = executeMyQuery("show databases", "DB1");
try
while (resultSet.next())
String actualValue = resultSet.getString("Database");
organisationDbNames.add(actualValue);
catch (SQLException e)
e.printStackTrace();
finally
closeConnections();
return organisationDbNames;
public static void deleteDataBasesByName(List<String> DataBasesNamesList)
try
connectSSH();
int dataBasesAmount = DataBasesNamesList.size();
for (int i = 0; i < dataBasesAmount; i++)
connectToDataBase(DataBasesNamesList.get(i));
Statement stmt = connection.createStatement();
stmt.executeUpdate("DROP database `" + DataBasesNamesList.get(i) + "`");
catch (SQLException e)
e.printStackTrace();
finally
CloseDataBaseConnection();
closeConnections();
【讨论】:
【参考方案5】:我的详细代码如下:
package mypackage;
import java.sql.*;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
public class UpdateMySqlDatabase
static int lport;
static String rhost;
static int rport;
public static void go()
String user = "ripon";
String password = "wasim";
String host = "myhost.ripon.wasim";
int port=22;
try
JSch jsch = new JSch();
Session session = jsch.getSession(user, host, port);
lport = 4321;
rhost = "localhost";
rport = 3306;
session.setPassword(password);
session.setConfig("StrictHostKeyChecking", "no");
System.out.println("Establishing Connection...");
session.connect();
int assinged_port=session.setPortForwardingL(lport, rhost, rport);
System.out.println("localhost:"+assinged_port+" -> "+rhost+":"+rport);
catch(Exception e)System.err.print(e);
public static void main(String[] args)
try
go();
catch(Exception ex)
ex.printStackTrace();
System.out.println("An example for updating a Row from Mysql Database!");
Connection con = null;
String driver = "com.mysql.jdbc.Driver";
String url = "jdbc:mysql://" + rhost +":" + lport + "/";
String db = "testDB";
String dbUser = "wasim";
String dbPasswd = "riponalwasim123";
try
Class.forName(driver);
con = DriverManager.getConnection(url+db, dbUser, dbPasswd);
try
Statement st = con.createStatement();
String sql = "UPDATE MyTableName " +
"SET email = 'ripon.wasim@smile.com' WHERE email='peace@happy.com'";
int update = st.executeUpdate(sql);
if(update >= 1)
System.out.println("Row is updated.");
else
System.out.println("Row is not updated.");
catch (SQLException s)
System.out.println("SQL statement is not executed!");
catch (Exception e)
e.printStackTrace();
【讨论】:
这个示例帮助我解决了我自己无法定位的盲错误。【参考方案6】:我的理解是,您想访问在远程机器上运行的 mysql 服务器,并通过 SSH 隧道监听端口 3306。
要使用命令行 ssh 客户端创建从本地计算机上的端口 1234 到远程计算机上的端口 3306 的隧道,您可以在本地计算机上键入以下命令:
ssh -L 1234:localhost:3306 mysql.server.remote
要在 Java 中做同样的事情,您可以使用JSch,这是 SSH2 的 Java 实现。来自其网站:
JSch 允许你连接到一个 sshd 服务器并使用端口转发、X11 转发、文件传输等,你可以将它的功能集成到你自己的 Java 程序中。 JSch 在 BSD 风格许可下获得许可。
例如,查看PortForwardingL.java
。会话连接后,使用 jdbc:mysql://localhost:1234/[database]
之类的东西作为连接 URL 创建与 MySQL 的 JDBC 连接。
【讨论】:
它对我不起作用.. 会话连接后,当我尝试从 java 连接到localhost:[port]
时,它会一直尝试类似,而不会给出任何异常,但它无法连接到数据库。任何帮助表示赞赏。以上是关于使用 Java 通过 SSH 连接到远程 MySQL 数据库的主要内容,如果未能解决你的问题,请参考以下文章
使用隧道 ssh 通过 nodeJS 中的 mongoose 通过 ssh 连接到远程服务器 mongoDB
使用 Python 通过 SSH 隧道连接到远程 PostgreSQL 数据库
在 Java 中通过 SSH 隧道连接到 Mongo 数据库