使用IAM身份validation和Spring JDBC(DataSource和JdbcTemplace)访问AWS RDS

我无法弄清楚如何实现这一点。 任何帮助和/或指针将不胜感激。

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

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

我被困的地方是我如何将其转换为Spring JDBC配置 。 也就是说,在我的@Configuration类中为此设置DataSourceJdbcTemplate bean。

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

—–编辑 – 开始—–

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

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

希望这更能说明我的需要。

—–编辑 – 结束—–

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提供的默认连接池。 它将每10分钟刷新一次令牌密码,因为令牌有效期为15分钟。 此外,它假定可以从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); }