尝试通过 SSH 隧道连接 PostgreSQL 时连接尝试失败。我将 Java 与 jsch 库一起使用

The connection attempt failed when try connect PostgreSQL via SSH tunnel. I using Java with jsch lib

我在成功连接 SSH 后尝试连接到 PostgreSQL 数据库时遇到问题。 我正在使用 Java + JSCH 库来帮助通过 SSH 隧道连接到 DB PostgreSQL。 请帮忙看看我的代码:

public static Connection connectDBAWS(String ipAddress, int port, String dbName, String username, String password, String tunnelHost, int tunnelPort, String tunnelUsername, String tunnelPassword) {

        Connection connection;
        Session session;

        try {

            //SSH TUNNEL connection
            JSch jsch = new JSch();

            session = jsch.getSession(tunnelUsername, tunnelHost, 22);
            session.setPassword(tunnelPassword);
            session.setConfig("StrictHostKeyChecking", "no");
            session.connect();
            System.out.println("Connected Successfully");
            int assinged_port = session.setPortForwardingL(port, tunnelHost, tunnelPort);
            System.out.println("assinged_port -> " + assinged_port);
            System.out.println("Port Forwarded");

            //DB connection
            String dbUrl = "jdbc:postgresql://" + ipAddress + ":" + assinged_port + "/" + dbName;
            Class.forName("org.postgresql.Driver");;
            connection = DriverManager.getConnection(dbUrl, username, password);
            System.out.println("connect DB success");
        } catch (Exception e) {
            connection = null;
            e.printStackTrace();
        }

        return connection;

    } 

在以下步骤抛出异常:

connection = DriverManager.getConnection(dbUrl, username, password);

这是结果:

Connected Successfully
assinged_port -> 5432
Port Forwarded
org.postgresql.util.PSQLException: The connection attempt failed.
    at org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl(ConnectionFactoryImpl.java:292)
    at org.postgresql.core.ConnectionFactory.openConnection(ConnectionFactory.java:49)
    at org.postgresql.jdbc.PgConnection.<init>(PgConnection.java:195)
    at org.postgresql.Driver.makeConnection(Driver.java:454)
    at org.postgresql.Driver.connect(Driver.java:256)
    at java.sql.DriverManager.getConnection(DriverManager.java:664)
    at java.sql.DriverManager.getConnection(DriverManager.java:247)
...

我使用上面的值手动连接 PostgreSQL,使用 pgAdmin4,并成功连接: See configuration image here.

请有人可以提供一些理想的方法来修复我的代码。

int assinged_port = session.setPortForwardingL(port, tunnelHost, tunnelPort);

我认为问题在于您不知道这些值中的任何一个应该意味着什么。您在错误的地方传递了错误的值。例如,assigned_port 值在这里毫无意义。

L 型端口转发背后的想法是:

  • 连接到 本地端口 port 通过 SSH 连接到您连接的服务器...
  • 然后弹出另一边。然后另一端将连接到主机 tunnelHost,端口 tunnelPort,因此转发将继续。

鉴于它的工作原理,名称和值都是错误的。

  • tunnelPort肯定是'port of the tunnel'。换句话说,这是本地端口 - 它应该是传递给 setPortForwardingL 第一个 参数,而不是最后一个
  • 第二个和第三个参数告诉远程 SSH 服务器要连接到什么,完成端口。鉴于您正在通过 SSH 连接到服务器,您不希望服务器随后连接到服务器。这有时有效,但通常无效,这取决于网络会话。你反而想要 localhost 那里!请记住,server/port 组合 连接到服务器端 。 'localhost' 这里的值指的是在服务器 上解析为本地主机 的内容。这就是你想要的。
  • 最后,SSH 隧道的要点是您在本地机器上有一个 local 端口 - 这就是隧道 'starts' 的位置。因此,您希望 JDBC 连接 到本地主机 。您正在连接到 ipAddress,无论它是什么。

所以,固定码是:

// The host in the cloud running an SSH server that you're connecting to.
String tunnelHost = "mypsqlserverinthecloud.com";
// The host in the cloud with the PSQL server, as you would
// connect to it from tunnelHost! In most cases this should be localhost!
String psqlHost = "localhost";
int psqlPort = 5432; // port of the psql server on the psql box.
int tunnelPort = 1234; // pick whatever you want here.
String username = "LDT";
String password = "hunter2"; // please see note below!

JSch session = new JSch();
session = jsch.getSession(username, tunnelHost, 22);
// Why aren't you using key-based authentication?
// Why aren't you using strict host key checking?
// None of this is particularly secure as a consequence!
session.setPassword(password);
session.setConfig("StrictHostKeyChecking", "no");
session.connect();

System.out.println("Connected Successfully");
session.setPortForwardingL(tunnelPort, psqlHost, psqlPort);
System.out.println("Port Forwarded");

String dbUrl = "jdbc:postgresql://localhost:" + tunnelPort + "/" + dbName;
// you do not need Class.forName. Not for the past 20 years.
// what stone age tutorial are you following?
// Consider that it probably contains more bad / obsolete advice!
Connection con = DriverManager.getConnection(dbUrl, username, password);
System.out.println("connect DB success");

这里还有多个代码风格错误。例如,如果您曾经编写过捕获异常的代码,e.printStackTrace() 它,然后继续运行,请停止您正在做的事情。这是可怕的,可怕的错误处理。只需让这些方法抛出已检查的异常即可。连接到 DB 的方法应该 抛出 SQLException 并且可能抛出 IOException。如果你真的不能那样做或者不想那样做,那么要么处理异常,对于 90% 以上的异常,你什么都想不到(这很正常),它应该总是以 throw.所以不是:

} catch (SQLException e) {
e.printStackTrace();
return null; // idiotic. Do not do this.
}

相反,执行:

public void theMethodThisCodeWasIn() throws SQLException {

或者如果你不能,请执行:

} catch (SQLException e) {
    throw new RuntimeException("Not handled", e);
}

例如。最好写下如何处理异常;抛出类型 RuntimeException 是最后的手段。但仍然 miles 优于 e.printStackTrace(); 并继续 运行!