gpt4 book ai didi

java - 使用 IAM 身份验证和 Spring JDBC 访问 AWS RDS(DataSource 和 JdbcTemplace)

转载 作者:行者123 更新时间:2023-12-02 01:14:52 30 4
gpt4 key购买 nike

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

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

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

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

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

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

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

想法是根据项目在任何 Web/应用服务器上使用此库。

希望这能进一步澄清我的需求。

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

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

类似于:

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 提供的默认连接池。由于 token 的有效期为 15 分钟,因此它将每 10 分钟刷新一次 token 密码。此外,它还假设可以从 DNS 主机名中提取区域。如果不是这种情况,您需要指定要使用的区域。

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);
}
}
}

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

要使用此数据源,您可以使用 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 配置:

@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 身份验证的支持:

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

关于java - 使用 IAM 身份验证和 Spring JDBC 访问 AWS RDS(DataSource 和 JdbcTemplace),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50847945/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com