如何利用aws security manager 和aws aurora 实现数据库密码定期更新且不需要重启应用程序
目前各个系统是用springboot+Hikari连接池+aws Aurora
您可以使用 AWS Secrets Manager 来管理数据库密码,并定期更新密码,而不需要重启应用程序。
以下是实现步骤:
在 AWS Secrets Manager 中创建一个新的密码。
在 AWS Aurora 数据库中创建一个新的用户,并授予此用户对所需数据库的访问权限。
将该用户的用户名和密码存储在 AWS Secrets Manager 中,命名为“db-credentials”。
在应用程序中,使用 AWS SDK for Java 访问 AWS Secrets Manager,并获取新的密码。
使用 Hikari 连接池来连接到 AWS Aurora 数据库,并将密码作为连接参数传递。
为了定期更新密码,可以创建 AWS Lambda 函数,定期(例如每个月)更新密码,并将新密码存储在 AWS Secrets Manager 中。
另外,您可以使用 AWS CloudWatch Events 来触发 Lambda 函数,以便在密码更新后通知您的团队。
请注意,您需要确保应用程序中的所有数据库连接都使用 Hikari 连接池,并且在连接参数中传递了密码。这样,当新密码在 AWS Secrets Manager 中更新时,Hikari 连接池将自动使用新密码来创建新的数据库连接。
需要确保应用程序中的所有数据库连接都使用 Hikari 连接池,并且在连接参数中传递了密码 这个示例
以下是一个示例,演示如何在应用程序中使用 Hikari 连接池来连接到 AWS Aurora 数据库,并使用 AWS Secrets Manager 中存储的密码进行身份验证:
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient;
import software.amazon.awssdk.services.secretsmanager.model.*;
import java.sql.Connection;
import java.sql.SQLException;
public class DatabaseManager {
// AWS Secrets Manager 中存储密码的密钥名称
private static final String SECRET_NAME = "db-credentials";
// AWS Secrets Manager 客户端
private static final SecretsManagerClient secretsManager = SecretsManagerClient.builder()
.region(Region.US_EAST_1)
.credentialsProvider(DefaultCredentialsProvider.create())
.build();
// Hikari 数据源
private static HikariDataSource dataSource;
// 初始化 Hikari 数据源
public static void init() {
// 从 AWS Secrets Manager 中获取数据库用户名和密码
String username = null;
String password = null;
GetSecretValueRequest getSecretValueRequest = GetSecretValueRequest.builder()
.secretId(SECRET_NAME)
.build();
GetSecretValueResponse getSecretValueResponse = secretsManager.getSecretValue(getSecretValueRequest);
if (getSecretValueResponse.secretString() != null) {
// 如果密码是字符串,则解析为用户名和密码
String secret = getSecretValueResponse.secretString();
username = JsonPath.read(secret, "$.username");
password = JsonPath.read(secret, "$.password");
} else {
// 如果密码是二进制数据,则将其解码为用户名和密码
ByteBuffer secretBinary = getSecretValueResponse.secretBinary();
// TODO: 解码二进制数据
}
// 创建 Hikari 数据源
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://your-aurora-cluster-endpoint:3306/your-database-name");
config.setUsername(username);
config.setPassword(password);
dataSource = new HikariDataSource(config);
}
// 获取数据库连接
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
// 关闭 Hikari 数据源
public static void close() {
dataSource.close();
}
}
在上述示例中,我们使用 AWS Secrets Manager 客户端从 Secrets Manager 中获取数据库用户名和密码,并使用这些凭据初始化 Hikari 数据源。然后,我们使用 Hikari 连接池来获取数据库连接。
请注意,在 Hikari 数据源的配置中,我们将用户名和密码设置为变量,这些变量将从 Secrets Manager 中获取。这样,当密码在 Secrets Manager 中更新时,Hikari 数据源将自动使用新密码来创建新的数据库连接。
以上代码如何集成到springboot应用里
您可以将上述 DatabaseManager 类集成到您的 Spring Boot 应用程序中,以便在应用程序启动时初始化 Hikari 数据源,并在需要时从数据源获取数据库连接。
以下是一个示例,演示如何将 DatabaseManager 类集成到 Spring Boot 应用程序中:
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>3.4.5</version>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>secretsmanager</artifactId>
<version>2.16.82</version>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.5.0</version>
</dependency>
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
// 初始化 DatabaseManager
@Bean(initMethod = "init", destroyMethod = "close")
public DatabaseManager databaseManager() {
return new DatabaseManager();
}
}
在上述配置类中,我们使用 @Bean 注释创建一个名为 "databaseManager" 的 bean,并调用其 init 方法初始化 Hikari 数据源。我们还使用 destroyMethod 属性指定在应用程序关闭时调用其 close 方法关闭数据源。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
@Component
public class MyService {
@Autowired
private DatabaseManager databaseManager;
public void doSomething() throws SQLException {
Connection connection = databaseManager.getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("SELECT * FROM my_table");
while (resultSet.next()) {
// 处理结果集
}
resultSet.close();
statement.close();
connection.close();
}
}
在上述示例中,我们使用 @Autowired 注释注入 DatabaseManager bean,并使用其 getConnection 方法获取数据库连接。我们使用此连接来执行 SQL 查询,并处理结果集。最后,我们关闭结果集、语句和连接。
请注意,此示例假定您已经在 AWS Aurora 中创建了名为 "my_table" 的表,并在应用程序中定义了相应的实体类。您需要根据您自己的数据库模式和实体类来修改此示例。
写一个starter让各个应用能方便使用
好的,以下是一个简单的 starter 示例,它集成了 AWS Secrets Manager 和 Hikari 连接池,并提供了一个简单的 API,以便在应用程序中获取数据库连接。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>3.4.5</version>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>secretsmanager</artifactId>
<version>2.16.82</version>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.5.0</version>
</dependency>
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.sql.SQLException;
@Configuration
@ConditionalOnClass(HikariDataSource.class)
@EnableConfigurationProperties(DatabaseProperties.class)
public class DatabaseAutoConfiguration {
@Autowired
private DatabaseProperties databaseProperties;
@Autowired(required = false)
private DataSourceProperties dataSourceProperties;
@Bean
@ConditionalOnMissingBean
public HikariDataSource dataSource() throws SQLException {
// 从 AWS Secrets Manager 中获取数据库用户名和密码
String username = null;
String password = null;
GetSecretValueRequest getSecretValueRequest = GetSecretValueRequest.builder()
.secretId(databaseProperties.getSecretName())
.build();
GetSecretValueResponse getSecretValueResponse = secretsManager.getSecretValue(getSecretValueRequest);
if (getSecretValueResponse.secretString() != null) {
// 如果密码是字符串,则解析为用户名和密码
String secret = getSecretValueResponse.secretString();
username = JsonPath.read(secret, "$.username");
password = JsonPath.read(secret, "$.password");
} else {
// 如果密码是二进制数据,则将其解码为用户名和密码
ByteBuffer secretBinary = getSecretValueResponse.secretBinary();
// TODO: 解码二进制数据
}
// 创建 Hikari 数据源配置
HikariConfig config = new HikariConfig();
if (dataSourceProperties != null) {
config.setDriverClassName(dataSourceProperties.getDriverClassName());
config.setJdbcUrl(dataSourceProperties.getUrl());
config.setUsername(dataSourceProperties.getUsername());
config.setPassword(dataSourceProperties.getPassword());
}
config.setJdbcUrl(databaseProperties.getUrl());
config.setUsername(username);
config.setPassword(password);
config.setMaximumPoolSize(databaseProperties.getMaximumPoolSize());
// 创建 Hikari 数据源
HikariDataSource dataSource = new HikariDataSource(config);
return dataSource;
}
}
在上述自动配置类中,我们使用 @EnableConfigurationProperties 注释来启用 DatabaseProperties 类中定义的属性,并使用 @ConditionalOnClass 注释来检查 HikariDataSource 是否存在。我们还使用 @ConditionalOnMissingBean 注释来检查是否已经存在名为 "dataSource" 的 bean。如果不存在,则创建 Hikari 数据源,并将其配置为从 AWS Secrets Manager 中获取密码。
请注意,在 Hikari 数据源的配置中,我们使用 dataSourceProperties 中的属性(如果存在)来设置数据库连接参数。如果 dataSourceProperties 为 null,则使用 DatabaseProperties 中定义的参数。这样,您可以在应用程序的配置文件中设置 dataSourceProperties 来覆盖 DatabaseProperties 中的默认值。
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "database")
public class DatabaseProperties {
// AWS Secrets Manager 中存储密码的密钥名称
private String secretName;
// 数据库连接 URL
private String url;
// 数据库连接池的最大大小
private int maximumPoolSize = 10;
// getter 和 setter
}
在上述属性类中,我们使用 @ConfigurationProperties 注释来将属性映射到 "database" 前缀下的配置文件中。我们定义了三个属性:secretName、url 和 maximumPoolSize。
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.sql.Connection;
import java.sql.SQLException;
@Service
public class DatabaseService {
@Autowired
private HikariDataSource dataSource;
public Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
}
在上述服务类中,我们使用 @Autowired 注释注入 HikariDataSource bean,并定义了一个 getConnection 方法,用于从数据源获取数据库连接。
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest
public class DatabaseAutoConfigurationTests {
@Autowired
private DatabaseService databaseService;
@Test
public void testGetConnection() throws SQLException {
Connection connection = databaseService.getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("SELECT 1");
assertThat(resultSet.next()).isTrue();
resultSet.close();
statement.close();
connection.close();
}
}
在上述测试类中,我们使用 @Autowired 注释注入 DatabaseService bean,并使用其 getConnection 方法获取数据库连接。我们执行一个简单的查询,以确保连接正常工作。
现在,您可以使用 mvn clean install 命令打包并安装 starter。然后,在您的 Spring Boot 应用程序中添加以下依赖项:
<dependency>
<groupId>com.example</groupId>
<artifactId>database-spring-boot-starter</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
然后,您可以使用 @Autowired 注释注入 DatabaseService bean,并使用其 getConnection 方法获取数据库连接,如下所示:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
@Component
public class MyService {
@Autowired
private DatabaseService databaseService;
public void doSomething() throws SQLException {
Connection connection = databaseService.getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("SELECT * FROM my_table");
while (resultSet.next()) {
// 处理结果集
}
resultSet.close();
statement.close();
connection.close();
}
}
请注意,在上述示例中,我们使用 @Autowired 注释注入 DatabaseService bean,并使用其 getConnection 方法获取数据库连接。我们使用此连接来执行 SQL 查询,并处理结果集。最后,我们关闭结果集、语句和连接。