为任何微服务安全地管理数据库连接字符串至关重要。通常,我们会使用环境变量来保护用户名和密码,却从未考虑过隐藏或掩码数据库主机名。在读写数据库实例中,有些组织会要求不得泄露主机名,并在应用程序启动时通过环境变量在运行时传递主机名。
本文讨论了如何在属性文件中通过环境变量配置主机名。
通过环境变量进行数据库配置
我们通常会以以下方式为 Spring 微服务配置默认连接字符串,数据库用户名和密码作为环境变量传递。
properties
server.port=8081
server.servlet.context-path=/api/e-sign/v1
spring.esign.datasource.jdbc-url=jdbc:mysql://localhost:3306/e-sign?allowPublicKeyRetrieval=true&useSSL=false
spring.esign.datasource.username=${DB_USER_NAME}
spring.esign.datasource.password=${DB_USER_PASSWORD}
spring.esign.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.esign.datasource.minimumIdle=5
spring.esign.datasource.maxLifetime=120000
如果我们的微服务连接到一个访问受限的安全数据库,并且数据库管理员或基础设施团队不希望你提供数据库主机名,那么我们就遇到了问题。通常,生产环境下数据库主机名可能如下所示:
properties
spring.esign.datasource.jdbc-url=jdbc:mysql://prod-db.fabrikam.com:3306/e-sign?allowPublicKeyRetrieval=true&useSSL=false
spring.esign.datasource.username=${DB_USER_NAME}
spring.esign.datasource.password=${DB_USER_PASSWORD}
使用 @Configuration 类
在这种情况下,管理员或云基础设施团队希望在容器启动时在运行时将主机名作为环境变量提供。其中一个选项是在配置类中构建并拼接连接字符串,如下所示:
java
@Configuration
public class DatabaseConfig {
private final Environment environment;
public DatabaseConfig(Environment environment) {
this.environment = environment;
}
@Bean
public DataSource databaseDataSource() {
String hostForDatabase = environment.getProperty("ESIGN_DB_HOST", "localhost:3306");
String dbUserName = environment.getProperty("DB_USER_NAME", "user-name");
String dbUserPassword = environment.getProperty("DB_USER_PASSWORD", "user-password");
String url = String.format("jdbc:mysql://%s/e-sign?allowPublicKeyRetrieval=true&useSSL=false", hostForDatabase);
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl(url);
dataSource.setUsername(dbUserName); // 替换为你的实际用户名
dataSource.setPassword(dbUserPassword); // 替换为你的实际密码
return dataSource;
}
}
上述方法可以工作,但我更倾向于使用 application.properties
的方法,这种方法易于使用且相当灵活。属性文件允许你以集中方式整合所有配置,使其更易于更新和管理。它还通过将配置与代码分离来提高可读性。DevOps 团队可以在不更改代码的情况下更新环境变量的值。
通常,我们使用环境变量来设置数据库用户名和密码,并在 application.properties
文件中使用相应的表达式占位符 ${}
。
properties
spring.esign.datasource.username=${DB_USER_NAME}
spring.esign.datasource.password=${DB_USER_PASSWORD}
然而,对于数据库 URL,我们只需要在环境变量中使用主机名,而不是连接字符串,因为不同微服务的每个连接字符串都会有不同的参数。因此,为了解决这个问题,Spring 允许你在连接字符串中使用占位符表达式,如下所示;这提供了灵活性,并且能够继续使用 application.properties
文件的方法,而不是通过数据库配置类来实现。
properties
spring.esign.datasource.jdbc-url=jdbc:mysql://${ESIGN_DB_HOST}:3306/e-sign?allowPublicKeyRetrieval=true&useSSL=false
一旦我们决定了上述方法,并且如果出于某种原因需要在较低环境中排查问题,我们就可以使用 ApplicationListener
接口来查看解析后的 URL:
java
@Component
public class ApplicationReadyLogger implements ApplicationListener<ApplicationReadyEvent> {
private final Environment environment;
public ApplicationReadyLogger(Environment environment) {
this.environment = environment;
}
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
String jdbcUrl = environment.getProperty("spring.esign.datasource.jdbc-url");
System.out.println("Resolved JDBC URL: " + jdbcUrl);
}
}
如果主机名配置存在问题,它将在应用程序启动时显示为错误。然而,在应用程序启动后,多亏了上述 ApplicationReadyLogger
实现,我们可以在应用程序日志中看到数据库 URL。请注意,我们不应在生产环境中这样做,因为基础设施团队希望对数据库写入主机名保密。
通过上述步骤,我们可以在 application.properties
文件的连接字符串中将数据库主机名配置为环境变量。
总结
使用环境变量为连接数据敏感数据库的主机名可以增强安全性和灵活性,并赋予云基础设施和 DevOps 团队更多权力。使用占位符表达式可以确保我们的配置保持清晰且易于维护。