Spring Boot Cloud Config无法解析Google Secret Manager令牌问题排查
问题
我运行着一个基于Spring Boot 2.6.10的Web应用,采用外部配置方案。配置服务器需要安全验证,凭证存储在Google Cloud Secret Manager中。但application.yml里Spring Cloud Config配置段的sm://标识无法被识别,配置段外的属性却能正常解析Secret Manager令牌。输入明文凭证时,配置可正常加载。想问是不是缺少依赖或者需要升级Spring Boot/Spring Cloud版本?
相关配置文件
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>io.my</groupId> <artifactId>smtest</artifactId> <version>0.0.1-SNAPSHOT</version> <name>Secret Manager Test</name> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.6.10</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <java.version>1.8</java.version> <spring-cloud.version>2021.0.3</spring-cloud.version> <spring-cloud-gcp.version>3.3.0</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>com.google.cloud</groupId> <artifactId>spring-cloud-gcp-starter-secretmanager</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>com.google.cloud</groupId> <artifactId>spring-cloud-gcp-dependencies</artifactId> <version>${spring-cloud-gcp.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>2.6.10</version> </plugin> </plugins> </build> </project>
application.yml
io: my: data: # 这些令牌可以正常解析 user: "${sm://my-gcp-project/my-data-user/latest}" password: "${sm://my-gcp-project/my-data-password/latest}" spring: application: name: smtest cloud: config: uri: https://my-external-config.io # 这些令牌无法解析 username: "${sm://my-gcp-project/ext-config-user/latest}" password: "${sm://my-gcp-project/ext-config-password/latest}" config: import: "configserver:"
原因与解决办法
核心原因
Spring Cloud Config客户端的配置加载优先级高于Spring Cloud GCP Secret Manager的属性解析器,导致spring.cloud.config.username、spring.cloud.config.password这类核心配置在Secret Manager解析器生效前就被读取,因此无法识别sm://标识。
方案1:通过环境变量/启动参数传递凭证
将Config服务器的凭证通过环境变量或JVM启动参数传入,这类参数会在更早的阶段被解析:
- 环境变量方式:
export SPRING_CLOUD_CONFIG_USERNAME=$(gcloud secrets versions access latest --secret="ext-config-user") export SPRING_CLOUD_CONFIG_PASSWORD=$(gcloud secrets versions access latest --secret="ext-config-password") - JVM参数方式:
java -jar smtest-0.0.1-SNAPSHOT.jar \ --spring.cloud.config.username=${sm://my-gcp-project/ext-config-user/latest} \ --spring.cloud.config.password=${sm://my-gcp-project/ext-config-password/latest}
方案2:升级Spring Cloud版本
当前使用的Spring Cloud版本是2021.0.3(Jubilee),升级到2021.0.4及以上版本,新版本优化了配置加载顺序,让Secret Manager解析器能够优先处理Config客户端的凭证配置。
方案3:自定义配置初始化器
创建一个配置初始化器,手动从Secret Manager获取凭证并注入到Spring Cloud Config属性中:
import com.google.cloud.spring.secretmanager.SecretManagerTemplate; import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; import org.springframework.context.ApplicationListener; import org.springframework.core.env.ConfigurableEnvironment; public class ConfigServerCredentialsInitializer implements ApplicationListener<ApplicationEnvironmentPreparedEvent> { private final SecretManagerTemplate secretManagerTemplate; public ConfigServerCredentialsInitializer(SecretManagerTemplate secretManagerTemplate) { this.secretManagerTemplate = secretManagerTemplate; } @Override public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) { ConfigurableEnvironment environment = event.getEnvironment(); // 从Secret Manager获取凭证 String configUser = secretManagerTemplate.getSecretString("my-gcp-project/ext-config-user/latest"); String configPassword = secretManagerTemplate.getSecretString("my-gcp-project/ext-config-password/latest"); // 将凭证设置到系统属性中,供Spring Cloud Config读取 environment.getSystemProperties().put("spring.cloud.config.username", configUser); environment.getSystemProperties().put("spring.cloud.config.password", configPassword); } }
在启动类中注册这个初始化器:
import com.google.cloud.spring.secretmanager.SecretManagerTemplate; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class SmtestApplication { public static void main(String[] args) { SpringApplication app = new SpringApplication(SmtestApplication.class); app.addListeners(new ConfigServerCredentialsInitializer(new SecretManagerTemplate())); app.run(args); } }
内容的提问来源于stack exchange,提问作者MiseryMachine
相关产品推荐
相关产品推荐

