【问题标题】:Configure a `DataSource` to connect to a managed Postgres server on Digital Ocean with SSL/TLS encryption配置 `DataSource` 以使用 SSL/TLS 加密连接到 Digital Ocean 上的托管 Postgres 服务器
【发布时间】:2021-03-21 15:03:14
【问题描述】:

我正在尝试DigitalOcean.com 上的managed database service,并创建了a Postgres instance

Digital Ocean 默认要求远程连接使用SSL(实际上是TLS)加密。

如何配置JDBC DataSource 实现以提供与此类数据库服务器的连接?

此问题类似于Produce a DataSource object for Postgres JDBC, programmatically,但添加了 SSL/TLS 加密和远程使用托管数据库的特定元素。

【问题讨论】:

    标签: java postgresql jdbc database-connection datasource


    【解决方案1】:

    获取DataSource 实现

    通常您的JDBC driver 提供javax.sql.DataSource 的实现。

    有关更多详细信息,请参阅my Answer 的类似问题。

    PGSimpleDataSource

    如果使用来自 jdbc.postgresql.org 的 JDBC 驱动程序,您可以使用 org.postgresql.ds.PGSimpleDataSource 类作为您的 DataSource 实现。

    实例化PGSimpleDataSource 类型的对象,然后调用setter 方法以提供连接到数据库服务器所需的所有信息。 DigitalOcean 网站上的网页列出了您特定数据库实例的所有这些信息。

    基本上是这样的:

    PGSimpleDataSource dataSource = new PGSimpleDataSource();
    
    dataSource.setServerName( "your-server-address-goes-here" );
    dataSource.setPortNumber( your-port-number-goes-here );
    dataSource.setDatabaseName( "defaultdb" );
    dataSource.setUser( "doadmin" );
    dataSource.setPassword( "your-password-goes-here" );
    

    多个服务器名称和端口号

    不幸的是,setServerNamesetPortNumber 方法的单数版本已被弃用。虽然烦人,但我们可能应该使用这些方法的复数版本(setServerNamessetPortNumbers),它们每个都采用一个数组。我们使用我们的服务器地址和 port number 使用数组文字填充一对单元素数组 String[]int[]

    • {“你的服务器地址去这里”}
    • { your-port-number-goes-here }
    PGSimpleDataSource dataSource = new PGSimpleDataSource();
    
    String[] serverAddresses = { "your-server-address-goes-here" };
    dataSource.setServerNames( serverAddresses );
    int[] serverPortNumbers = { your-port-number-goes-here };
    dataSource.setPortNumbers( serverPortNumbers );
    dataSource.setDatabaseName( "defaultdb" );
    dataSource.setUser( "doadmin" );
    dataSource.setPassword( "your-password-goes-here" );
    

    SSL/TLS 加密

    最后,我们需要为问题中提到的 SSL/TLS 加密添加信息。

    讽刺的是,打电话给setSsl( true )

    你会认为我们会调用setSsl 方法并传递true。但不是。虽然违反直觉,但将其设置为 true 会导致您的连接尝试失败。我不知道为什么会这样。但反复试验让我避免了那个电话。

    CA 证书

    为了让您的客户端 Java 应用启动 SSL/TLS 连接,JDBC 驱动程序必须有权访问 Digital Ocean 使用的 CA 证书。在您的 Digital Ocean 管理页面上,单击 Download CA certificate 链接以下载一个小文本文件。该文件将命名为ca-certificate.crt

    我们需要将该文件的文本作为字符串提供给我们的 JDBC 驱动程序。执行以下操作将文件加载到文本字符串中。

    // Get CA certificate used in TLS connections.
    Path path = Paths.get( "/Users/basilbourque/Downloads/ca-certificate.crt" );
    String cert = null;
    try
    {
        cert = Files.readString( path , StandardCharsets.UTF_8 );
        System.out.println( "cert = " + cert );
    }
    catch ( IOException ex )
    {
        throw new IllegalStateException( "Unable to load the TLS certificate needed to make database connections." );
    }
    Objects.requireNonNull( cert );
    if ( cert.isEmpty() ) {throw new IllegalStateException( "Failed to load TLS cert." ); }
    

    通过调用DataSource::setSslCert 将该CA 证书文本传递给您的JDBC 驱动程序。请注意,我们调用setSslRootCert。不要混淆这两个名称相似的方法。

    dataSource.setSslCert( cert );
    

    测试连接

    最后,我们可以使用配置的DataSource 对象来连接数据库。该代码将如下所示:

    // Test connection
    try (
            Connection conn = dataSource.getConnection() ;
            // …
    )
    {
        System.out.println( "DEBUG - Postgres connection made. " + Instant.now() );
        // …
    }
    catch ( SQLException e )
    {
        e.printStackTrace();
    }
    

    完整示例代码

    把所有这些放在一起,看看这样的东西。

    // Get CA certificate used in TLS connections.
    Path path = Paths.get( "/Users/basilbourque/Downloads/ca-certificate.crt" );
    String cert = null;
    try
    {
        cert = Files.readString( path , StandardCharsets.UTF_8 );
        System.out.println( "cert = " + cert );
    }
    catch ( IOException ex )
    {
        throw new IllegalStateException( "Unable to load the TLS certificate needed to make database connections." );
    }
    Objects.requireNonNull( cert );
    if ( cert.isEmpty() ) {throw new IllegalStateException( "Failed to load TLS cert." ); }
    
    // PGSimpleDataSource configuration
    PGSimpleDataSource dataSource = new PGSimpleDataSource();
    
    String[] serverAddresses = { "your-server-address-goes-here" };
    dataSource.setServerNames( serverAddresses );
    int[] serverPortNumbers = { your-port-number-goes-here };
    dataSource.setPortNumbers( serverPortNumbers );
    dataSource.setSslCert( cert );
    dataSource.setDatabaseName( "defaultdb" );
    dataSource.setUser( "doadmin" );
    dataSource.setPassword( "your-password-goes-here" );
    
    // Test connection
    try (
    Connection conn = dataSource.getConnection() ;
    // …
    )
    {
    System.out.println( "DEBUG - Postgres connection made. " + Instant.now() );
    // …
    }
    catch ( SQLException e )
    {
    e.printStackTrace();
    }
    

    可信来源

    在 DigitalOcean.com 上创建 Postgres 实例时,您将能够限制传入的连接请求。您可以指定 Postgres 服务器预期的单个 IP 地址,这是 Digital Ocean 行话中的“可信来源”。任何试图连接到您的 Postgres 服务器的黑客都将被忽略,因为他们来自其他 IP 地址。

    提示:在此 IP 地址编号的数据输入字段内单击以使网页自动检测并提供您的网络浏览器当前使用的 IP 地址。如果您在同一台计算机上运行 Java 代码,请使用相同的 IP 号码作为您的单一可信来源。

    否则,请输入运行 Java JDBC 代码的计算机的 IP 地址。

    其他问题

    我没有详细说明,例如用于管理 CA 证书文件的适当安全协议,或者通过从 JNDI 服务器而不是 hard-coding 获取 DataSource 对象来外部化您的连接信息。但希望以上示例可以帮助您入门。

    【讨论】:

      猜你喜欢
      • 2021-06-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-05-24
      相关资源
      最近更新 更多