使用 IAM 身份验证和 Spring JDBC(DataSource 和 JdbcTemplace)访问 AWS RDS [英] Accessing AWS RDS using IAM Authentication and Spring JDBC (DataSource and JdbcTemplace)

查看:45
本文介绍了使用 IAM 身份验证和 Spring JDBC(DataSource 和 JdbcTemplace)访问 AWS RDS的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我不知道如何实现这一点.任何帮助和/或指示将不胜感激.

I am not able to figure out how to implement this. Any help and/or pointers will be greatly appreciated.

目前,我的 Java/Spring 应用程序后端部署在 EC2 上,并使用常规 Spring JDBC 设置成功访问 RDS 上的 MySQL.即在application.properties中存储数据库信息,在@Configuration类中配置DataSourceJdbcTemplate.一切正常.

Currently, my Java/Spring application backend is deployed on EC2 and accessing MySQL on RDS successfully using the regular Spring JDBC setup. That is, storing database info in application.properties and configuring DataSource and JdbcTemplate in @Configuration class. Everything works fine.

现在,我需要安全地访问 RDS 上的 MySQL.RDS 实例启用了 IAM 身份验证.我还成功创建了IAM 角色 并应用了内联策略.然后,按照 上的 AWS RDS 文档和 Java 示例这个链接,我能够成功地使用身份验证令牌和我创建的用户而不是常规的数据库用户名和密码从独立Java类访问数据库.这个独立的 Java 类直接处理Connection"对象.

Now, I need to access MySQL on RDS securely. RDS instance has IAM Authentication enabled. I have also successfully created IAM role and applied inline policy. Then, following the AWS RDS documentation and Java example on this link, I am able to access the database from a standalone Java class successfully using Authentication Token and the user I created instead of regular db username and password. This standalone Java class is dealing with "Connection" object directly.

我遇到的问题是我如何将其转换为 Spring JDBC 配置.也就是说,在我的@Configuration 类中为此设置 DataSourceJdbcTemplate bean.

The place I am stuck is how I translate this to Spring JDBC configuration. That is, setting up DataSource and JdbcTemplate beans for this in my @Configuration class.

实现这一点的正确/正确方法是什么?

What would be a correct/right approach to implement this?

----- 编辑 - 开始 -----

我正在尝试将其实现为可用于多个项目的库.也就是说,它将用作 JAR 并在项目的 POM 文件中声明为依赖项.该库将包括可配置的 AWS 服务,例如使用通用数据库用户名和密码的 RDS 访问、使用 IAM 身份验证的 RDS 访问、用于数据加密的 KMS(CMK/数据密钥)等.

I am trying to implement this as a library that can be used for multiple projects. That is, it will be used as a JAR and declared as a dependency in a project's POM file. This library is going to include configurable AWS Services like this RDS access using general DB username and password, RDS access using IAM Authentication, KMS (CMK/data keys) for data encryption, etc.

想法是根据项目在任何网络/应用服务器上使用这个库.

Idea is to use this library on any web/app server depending on the project.

希望这更能说明我的需求.

Hope this clarifies my need more.

----- 编辑 - 结束 -----

DataSource 内部有 getConnection() 所以我基本上可以创建我自己的 DataSource 实现来实现我想要的.但这是一个好方法吗?

DataSource internally has getConnection() so I can basically create my own DataSource implementation to achieve what I want. But is this a good approach?

类似于:

public class MyDataSource implements DataSource {
    @Override
    public Connection getConnection() throws SQLException {
        Connection conn = null;
        // get a connection using IAM Authentication Token for accessing AWS RDS, etc. as in the AWS docs
        return conn;
    }

    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        return getConnection();
    }

    //other methods
} 

推荐答案

您可以使用以下代码段替换 SpringBoot/Tomcat 提供的默认连接池.它将每 10 分钟刷新一次令牌密码,因为令牌的有效期为 15 分钟.此外,它假设可以从 DNS 主机名中提取区域.如果不是这种情况,您需要指定要使用的区域.

You can use the following snippet as a replacement for the default connection-pool provided by SpringBoot/Tomcat. It will refresh the token password every 10 minutes, since the token is valid for 15 minutes. Also, it assumes the region can be extracted from the DNS hostname. If this is not the case, you'll need to specify the region to use.

public class RdsIamAuthDataSource extends org.apache.tomcat.jdbc.pool.DataSource {

private static final Logger LOG = LoggerFactory.getLogger(RdsIamAuthDataSource.class);

/**
 * The Java KeyStore (JKS) file that contains the Amazon root CAs
 */
public static final String RDS_CACERTS = "/rds-cacerts";
/**
 * Password for the ca-certs file.
 */
public static final String PASSWORD = "changeit";
public static final int DEFAULT_PORT = 3306;

@Override
public ConnectionPool createPool() throws SQLException {
    return pool != null ? pool : createPoolImpl();
}

protected synchronized ConnectionPool createPoolImpl() throws SQLException {
    return pool = new RdsIamAuthConnectionPool(poolProperties);
}

public static class RdsIamAuthConnectionPool extends ConnectionPool implements Runnable {

    private RdsIamAuthTokenGenerator rdsIamAuthTokenGenerator;
    private String host;
    private String region;
    private int port;
    private String username;
    private Thread tokenThread;

    public RdsIamAuthConnectionPool(PoolConfiguration prop) throws SQLException {
        super(prop);
    }

    @Override
    protected void init(PoolConfiguration prop) throws SQLException {
        try {
            URI uri = new URI(prop.getUrl().substring(5));
            this.host = uri.getHost();
            this.port = uri.getPort();
            if (this.port < 0) {
                this.port = DEFAULT_PORT;
            }
            this.region = StringUtils.split(this.host,'.')[2]; // extract region from rds hostname
            this.username = prop.getUsername();
            this.rdsIamAuthTokenGenerator = RdsIamAuthTokenGenerator.builder().credentials(new DefaultAWSCredentialsProviderChain()).region(this.region).build();
            updatePassword(prop);
            final Properties props = prop.getDbProperties();
            props.setProperty("useSSL","true");
            props.setProperty("requireSSL","true");
            props.setProperty("trustCertificateKeyStoreUrl",getClass().getResource(RDS_CACERTS).toString());
            props.setProperty("trustCertificateKeyStorePassword", PASSWORD);
            super.init(prop);
            this.tokenThread = new Thread(this, "RdsIamAuthDataSourceTokenThread");
            this.tokenThread.setDaemon(true);
            this.tokenThread.start();
        } catch (URISyntaxException e) {
            throw new RuntimeException(e.getMessage());
        }
    }

    @Override
    public void run() {
        try {
            while (this.tokenThread != null) {
                Thread.sleep(10 * 60 * 1000); // wait for 10 minutes, then recreate the token
                updatePassword(getPoolProperties());
            }
        } catch (InterruptedException e) {
            LOG.debug("Background token thread interrupted");
        }
    }

    @Override
    protected void close(boolean force) {
        super.close(force);
        Thread t = tokenThread;
        tokenThread = null;
        if (t != null) {
            t.interrupt();
        }
    }

    private void updatePassword(PoolConfiguration props) {
        String token = rdsIamAuthTokenGenerator.getAuthToken(GetIamAuthTokenRequest.builder().hostname(host).port(port).userName(this.username).build());
        LOG.debug("Updated IAM token for connection pool");
        props.setPassword(token);
    }
}
}

请注意,您需要导入亚马逊的根/中间证书才能建立可信连接.上面的示例代码假设证书已导入到名为rds-cacert"的文件中,并且在类路径中可用.或者,您也可以将它们导入到 JVM 的cacerts"文件中.

Please note that you'll need to import Amazon's root/intermediate certificates to establish a trusted connection. The example code above assumes that the certificates have been imported into a file called 'rds-cacert' and is available on the classpath. Alternatively, you can also import them into the JVM 'cacerts' file.

要使用此数据源,您可以使用 Spring 的以下属性:

To use this data-source, you can use the following properties for Spring:

datasource:
  url: jdbc:mysql://dbhost.xyz123abc.us-east-1.rds.amazonaws.com/dbname
  username: iam_app_user
  driver-class-name: com.mysql.cj.jdbc.Driver
  type: com.mydomain.jdbc.RdsIamAuthDataSource

使用 Spring Java 配置:

Using Spring Java config:

@Bean public DataSource dataSource() { 
    PoolConfiguration props = new PoolProperties(); 
    props.setUrl("jdbc:mysql://dbname.abc123xyz.us-east-1.rds.amazonaws.com/dbschema"); 
    props.setUsername("iam_dbuser_app"); 
    props.setDriverClassName("com.mysql.jdbc.Driver"); 
    return new RdsIamAuthDataSource(props); 
}

更新:在使用 MySQL 时,您还可以决定使用 MariaDB JDBC 驱动程序,它内置了对 IAM 身份验证的支持:

UPDATE: When using MySQL, you can also decide to use the MariaDB JDBC driver, which has builtin support for IAM authentication:

spring:
  datasource:
    host: dbhost.cluster-xxx.eu-west-1.rds.amazonaws.com
    url: jdbc:mariadb:aurora//${spring.datasource.host}/db?user=xxx&credentialType=AWS-IAM&useSsl&serverSslCert=classpath:rds-combined-ca-bundle.pem
    type: org.mariadb.jdbc.MariaDbPoolDataSource

以上需要 MariaDB 和 AWS SDK 库,并且需要类路径中的 CA-bundle

The above requires MariaDB and AWS SDK libraries, and needs the CA-bundle in the classpath

这篇关于使用 IAM 身份验证和 Spring JDBC(DataSource 和 JdbcTemplace)访问 AWS RDS的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆