透過 SSL 連接 PostgreSQL 資料庫
一、簡介
在資料庫管理領域,確保應用程式和資料庫之間的安全通訊非常重要。在本教程中,我們將了解如何從 JDBC 和 Spring Boot 透過 SSL 連接到 PostgreSQL。
2.PostgreSQL配置
我們需要更新 PostgreSQL 伺服器以允許透過 SSL 進行連線。為此,我們需要準備好或建立根(CA)憑證、伺服器憑證和私鑰。讓我們修改 PostgreSQL 伺服器設定檔postgresql.conf
並提供憑證檔案的路徑:
`...
ssl = on
ssl_ca_file = '/opt/homebrew/var/postgresql@14/rootCA.crt'
ssl_cert_file = '/opt/homebrew/var/postgresql@14/localhost.crt'
#ssl_crl_file = ''
#ssl_crl_dir = ''
ssl_key_file = '/opt/homebrew/var/postgresql@14/localhost.key'
...`
現在,讓我們修改 PostgreSQL客戶端設定檔pg_hba.conf
並在 IPv4 部分下新增以下內容:
`...
IPv4 local connections:
hostssl all all 0.0.0.0/0 cert
...`
3.Maven配置
讓我們將PostgreSQL JDBC 驅動程式相依性新增至pom.xml
檔案中以連接到伺服器:
`
`
在撰寫本文時,我們正在使用最新版本的驅動程序,以便我們能夠使用 pkcs-12 用戶端憑證格式。
4. 從 JDBC 連接
需要客戶端憑證才能透過 SSL 建立安全連線。因此,我們需要準備好客戶端憑證和金鑰文件,並使用與生成伺服器憑證相同的根憑證建立憑證。
但是,我們不能使用私鑰直接從 JDBC 連接,因此,我們需要將私鑰匯出為“pkcs-8”相容格式:
openssl pkcs8 -topk8 -inform PEM -outform DER -in certs/pg_client.key -out certs/pg_client.pk8 -nocrypt
使用導出的金鑰,我們可以透過定義以下屬性來正確連接到 PostgreSQL 伺服器:
- 使用者名稱和密碼
- 證書位置
- pkcs-8 關鍵位置,最後
- 根 CA 憑證位置。
為了示範這一點,讓我們建立一個帶有名為checkConnectionSsl
的方法的PgJdbc
類別:
public class PgJdbc {
public void checkConnectionSsl(String url, String username, String password, Map<String, String> extraProps) {
Properties props = new Properties();
props.putAll(extraProps);
props.put("username", username);
props.put("password", password);
props.put("sslmode", "verify-ca");
props.put("ssl", "true");
try (Connection connection = DriverManager.getConnection(url, props)) {
if (!connection.isClosed()) {
connection.close();
}
System.out.println("Connection was successful");
} catch (SQLException e) {
System.out.println("Connection failed");
}
}
// ...
}
checkConnectionSsl
方法採用連線所需的參數。根據我們想要的連接方式,我們將透過extraProps
屬性傳遞適當的鍵值對。我們將ssl
屬性設為true
來指示我們要使用 SSL 進行連接,並且sslmode
屬性指定憑證驗證的類型。
讓我們新增一個main
方法並嘗試建立連線:
public class PgJdbc {
// ...
public static void main(String[] args) {
PgJdbc pg = new PgJdbc();
String url = "jdbc:postgresql://localhost:5432/testdb";
String username = "postgres";
String password = "password";
String BASE_PATH = Paths.get("certs")
.toAbsolutePath()
.toString();
Map<String, String> connectionProperties = Map.of(
"sslcert", BASE_PATH.concat("/pg_client.crt"),
"sslkey", BASE_PATH.concat("/pg_client.pk8"),
"sslrootcert", BASE_PATH.concat("/root.crt"));
System.out.println("Connection without keystore and truststore");
pg.checkConnectionSsl(url, username, password, connectionProperties);
}
}
$ mvn clean compile -q exec:java -Dexec.mainClass="com.baeldung.pgoverssl.PgJdbc"
Connection was successful
從上面的輸出可以看出,我們已經能夠建立成功的連結。
5. 使用密鑰庫從 JDBC 連接
也可以使用金鑰庫和信任庫建立相同的連線。然而,這需要將客戶端憑證和私密金鑰轉換為 pkcs-12 相容格式,然後使用 Java 隨附的 keytool 公用程式從中建立金鑰庫和根 CA 憑證建立信任庫。
讓我們將憑證和金鑰匯出為 pkcs-12 格式:
$ openssl pkcs12 -export -in certs/pg_client.crt -inkey certs/pg_client.key -out certs/pg_client.p12 -name postgres
使用匯出的證書,讓我們建立一個金鑰庫:
`$ keytool -importkeystore -destkeystore certs/pg_client.jks -srckeystore certs/pg_client.p12 -srcstoretype pkcs12
Importing keystore certs/pg_client.p12 to certs/pg_client.jks...
Enter destination keystore password:
...
Import command completed: 1 entries successfully imported, 0 entries failed or cancelled`
最後,我們可以建立信任庫:
`$ keytool -import -alias server -file certs/root.crt -keystore certs/truststore.jks -storepass password
...
Certificate was added to keystore`
現在有了金鑰庫和信任庫,讓我們修改main
方法並嘗試建立連線:
public class PgJdbc {
// ...
public static void main(String[] args) {
// ...
System.setProperty("javax.net.ssl.keyStore", BASE_PATH.concat("/pg_client.jks"));
System.setProperty("javax.net.ssl.keyStorePassword", "password");
System.setProperty("javax.net.ssl.trustStore", BASE_PATH.concat("/truststore.jks"));
System.setProperty("javax.net.ssl.trustStorePassword", "password");
System.out.println("\nConnection using keystore and truststore");
pg.checkConnectionSsl(url, username, password, Map.of("sslfactory", "org.postgresql.ssl.DefaultJavaSSLFactory"));
}
}
請注意,我們提供了四個System
屬性,其中兩個是密碼。這些密碼是在建立金鑰庫和信任庫時提供的。此外,我們必須向DefaultJavaSSLFactory
提供sslfactory
參數以進行驗證。
我們再測試一下:
Connection using keystore and truststore
Connection was successful
這也是一次成功的連結。
6. 從 Spring Boot 連接
以類似的方式,我們可以從 Spring Boot 應用程式透過 SSL 進行連線。讓我們將基本 Spring Boot 應用程式所需的依賴項新增到pom.xml
檔案中:
`
// ...
我們需要一個基本的 Spring Boot 入門類別:
`@SpringBootApplication
public class PgSpringboot {
public static void main(String[] args) {
SpringApplication.run(PgSpringboot.class, args);
}
}`
接下來,讓我們使用連線所需的屬性來設定application.yaml
檔案:
`spring:
application:
name: postgresqlssltest
datasource:
url: jdbc:postgresql://localhost:5432/testdb?ssl=true&sslmode=verify-ca&sslrootcert=certs/root.crt&sslcert=certs/pg_client.crt&sslkey=certs/pg_client.pk8
username: postgres
password: "password"
driver-class-name: org.postgresql.Driver
jpa:
hibernate:
ddl-auto: update
database-platform: org.hibernate.dialect.PostgreSQLDialect`
讓我們嘗試透過運行應用程式連接到 PostgreSQL 伺服器:
$ mvn clean compile -q exec:java -Dexec.mainClass="com.baeldung.pgoverssl.PgSpringboot" -Dspring.config.location=classpath:./pgoverssl/application.yaml
...
2024-07-04T21:41:17.552+01:00 INFO 458 --- [postgresqlssltest] [ringboot.main()] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2024-07-04T21:41:18.290+01:00 INFO 458 --- [postgresqlssltest] [ringboot.main()] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection org.postgresql.jdbc.PgConnection@4e331d3d
2024-07-04T21:41:18.291+01:00 INFO 458 --- [postgresqlssltest] [ringboot.main()] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
...
就像我們從 JDBC 連線一樣,我們已經能夠使用 Spring Boot 成功建立與 PostgreSQL 伺服器的連線。
七、結論
在本文中,我們透過 SSL 設定並安全地建立了與 PostgreSQL 伺服器的資料庫連線。然而,值得注意的是,我們在範例中實現的連接選項不一定是詳盡的。
完整的範例可以在 GitHub 上找到