SpringBoot项目从Azure Key Vault获取Secret返回空值求助
SpringBoot从Azure Key Vault获取Secret为空的排查与解决
问题背景
示例SpringBoot项目可正常从Azure Key Vault读取Secret,但无控制器的实际数据库连接项目中,@Value注入的Secret始终为空。已确认Java版本、依赖、Azure凭据配置正确,连测试Secret都无法获取,目标是读取数据库凭据。
示例项目配置与代码
application.properties
server.port=8090 spring.cloud.azure.keyvault.secret.property-sources[0].credential.client-id= xxxxxx spring.cloud.azure.keyvault.secret.property-sources[0].credential.client-secret=xxxxx spring.cloud.azure.keyvault.secret.property-sources[0].endpoint=https:xxxxxx spring.cloud.azure.keyvault.secret.property-sources[0].profile.tenant-id=xxxxxx
启动类
@SpringBootApplication @RestController public class DatafeedKeyvaultApplication implements CommandLineRunner{ @Value("${connectionString}") private String connectionString; public static void main(String[] args) { SpringApplication.run(DatafeedKeyvaultApplication.class, args); } @GetMapping("get") public String get() { return connectionString; } public void run(String... varl) throws Exception { System.out.println(String.format("\nConnection String stored in Azure Key Vault:\n%s\n",connectionString)); } }
实际项目配置与代码
application.properties
#azure.keyvault.enabled=true #spring.cloud.azure.keyvault.secret.property-sources[0].enabled=true spring.cloud.azure.keyvault.secret.property-sources[0].credential.client-id=xxxxxx spring.cloud.azure.keyvault.secret.property-sources[0].credential.client-secret=xxxx spring.cloud.azure.keyvault.secret.property-sources[0].endpoint=xxxxxx spring.cloud.azure.keyvault.secret.property-sources[0].profile.tenant-id=xxxxx server.port=8090 spring.jpa.properties.hibernate.default_schema=acn spring.jpa.properties.hibernate.format_sql = false spring.jpa.show-sql=false spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl spring.jpa.hibernate.ddl-auto = update spring.jackson.default-property-inclusion = NON_NULL spring.main.web-application-type=NONE spring.main.banner-mode = off logging.level.root=error ################ MSPS DB Configuration ################ INTEGRATION_MSPS_DBSERVER_URL=xxxxxxx MSPS_DB_INTEGRATED_SECURTY =false spring.datasource.driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver spring.datasource.url=jdbc:sqlserver://${INTEGRATION_MSPS_DBSERVER_URL};databaseName=ACN_MSPSJiraInteg;integratedSecurity=${MSPS_DB_INTEGRATED_SECURTY}; spring.datasource.username= xxxxx spring.datasource.password=xxxxx
启动类
@SpringBootApplication public class JiraMspsUtilityApplication implements CommandLineRunner { @Value("${datafeed-sample-value}") private static String keyvault_value; private static final Logger loggerForWorkitemMetrics = LoggerFactory.getLogger("metricsUpdatAppender"); private static final Logger LoggerForSharedPlanUpdate = LoggerFactory.getLogger("sharedPlanUpdateAppender"); @Autowired ConfigProperties configProp; @Autowired JIRAMSPSService jiraMspsService; private static final Logger logger = LoggerFactory.getLogger("JiraMspsUtilityApplication"); static { System.out.println("key vault value "+ keyvault_value); logger.info("key vault value "+ keyvault_value); boolean isExistEC = false; Provider[] providers = Security.getProviders(); for (Provider provider : providers) { logger.info("CRYPTO provider: " + provider.getName().toString()); Set<Provider.Service> services = provider.getServices(); for (Provider.Service service : services) { logger.info("CRYPTO algorithm: " + service.getAlgorithm()); if (service.getAlgorithm().equals("EC")) { isExistEC = true; } } } if (isExistEC) { logger.info("BC security provider not added for EC algorithm as JRE already able to find it"); } else { Security.addProvider(new BouncyCastleProvider()); logger.info("BC security provider added for EC algorithm"); } } public static void main(String[] args) { SpringApplication app = new SpringApplication(JiraMspsUtilityApplication.class); app.run(args); System.out.println(String.format("\n1. Key Valut value stored in Azure Key Vault:\n%s\n",keyvault_value)); logger.info("\n1. Key Valut value stored in Azure Key Vault:\n%s\n",keyvault_value); } @Override public void run(String... args) throws IOException { System.out.println(String.format("\n2. Key Valut value stored in Azure Key Vault:\n%s\n",keyvault_value)); logger.info("\n2. Key Valut value stored in Azure Key Vault:\n%s\n",keyvault_value); if (args.length == 0) { loggerForWorkitemMetrics.info("There were no commandline arguments passed"); LoggerForSharedPlanUpdate.info("There were no commandline arguments passed"); System.exit(1); } else if (args.length > 1) { loggerForWorkitemMetrics.info("More than one argument passed"); LoggerForSharedPlanUpdate.info("More than one argument passed"); System.exit(1); } else { if (Integer.parseInt(args[0]) == 0) { boolean flag = jiraMspsService.updateWorkRequestMetricsInJira(); if (flag) { System.exit(0); } else { loggerForWorkitemMetrics.info("WORK REQUEST METRICS UPDATE PROCESS INCOMPLETE DUE TO ERROR"); } } else if (Integer.parseInt(args[0]) == 1) { boolean flag = jiraMspsService.updateSharedWorkplanEntries(); if (flag) { System.exit(0); } else { LoggerForSharedPlanUpdate.info("SHARED PLANS UPDATE PROCESS INCOMPLETE DUE TO ERROR"); } } else { loggerForWorkitemMetrics.info( "Invalid argument(s) passed."); LoggerForSharedPlanUpdate.info( "Invalid argument(s) passed."); System.exit(1); } } } }
核心问题排查与修复
1. 静态变量无法被Spring依赖注入
实际项目中keyvault_value是静态变量,Spring的@Value仅支持注入实例字段,静态变量属于类级别,不受Spring容器管理,因此无法注入值。
修复方式:
- 直接移除
static修饰符,改为实例变量:
@Value("${datafeed-sample-value}") private String keyvault_value;
- 若必须保留静态变量,通过实例方法赋值:
private static String keyvault_value; @Value("${datafeed-sample-value}") public void setKeyvault_value(String value) { keyvault_value = value; }
2. 静态代码块执行时机过早
静态代码块在类加载时就执行,此时Spring容器尚未初始化完成,@Value注入还未执行,因此keyvault_value必然为空。
修复方式:
将静态代码块中的逻辑移到@PostConstruct注解的方法中(Spring容器初始化完成后执行):
@PostConstruct public void initSecurityProvider() { System.out.println("key vault value "+ keyvault_value); logger.info("key vault value "+ keyvault_value); boolean isExistEC = false; Provider[] providers = Security.getProviders(); for (Provider provider : providers) { logger.info("CRYPTO provider: " + provider.getName().toString()); Set<Provider.Service> services = provider.getServices(); for (Provider.Service service : services) { logger.info("CRYPTO algorithm: " + service.getAlgorithm()); if (service.getAlgorithm().equals("EC")) { isExistEC = true; } } } if (isExistEC) { logger.info("BC security provider not added for EC algorithm as JRE already able to find it"); } else { Security.addProvider(new BouncyCastleProvider()); logger.info("BC security provider added for EC algorithm"); } }
3. 主方法中直接访问注入字段的问题
主方法是静态方法,调用app.run(args)后容器刚启动,实例字段的注入可能尚未完成,此时直接访问keyvault_value会得到空值。所有依赖注入的字段操作都应放在CommandLineRunner的run方法或@PostConstruct方法中。
4. 显式开启Key Vault配置
实际项目中注释掉了Key Vault的启用配置,建议显式开启以避免默认配置失效:
azure.keyvault.enabled=true spring.cloud.azure.keyvault.secret.property-sources[0].enabled=true
5. 数据库凭据替换验证
修复上述问题后,将数据库的用户名和密码替换为Key Vault中的Secret:
spring.datasource.username=${your-keyvault-username-key} spring.datasource.password=${your-keyvault-password-key}
修改后的实际项目启动类示例
@SpringBootApplication public class JiraMspsUtilityApplication implements CommandLineRunner { @Value("${datafeed-sample-value}") private String keyvault_value; private static final Logger loggerForWorkitemMetrics = LoggerFactory.getLogger("metricsUpdatAppender"); private static final Logger LoggerForSharedPlanUpdate = LoggerFactory.getLogger("sharedPlanUpdateAppender"); @Autowired ConfigProperties configProp; @Autowired JIRAMSPSService jiraMspsService; private static final Logger logger = LoggerFactory.getLogger("JiraMspsUtilityApplication"); @PostConstruct public void initSecurityProvider() { System.out.println("key vault value "+ keyvault_value); logger.info("key vault value "+ keyvault_value); boolean isExistEC = false; Provider[] providers = Security.getProviders(); for (Provider provider : providers) { logger.info("CRYPTO provider: " + provider.getName().toString()); Set<Provider.Service> services = provider.getServices(); for (Provider.Service service : services) { logger.info("CRYPTO algorithm: " + service.getAlgorithm()); if (service.getAlgorithm().equals("EC")) { isExistEC = true; } } } if (isExistEC) { logger.info("BC security provider not added for EC algorithm as JRE already able to find it"); } else { Security.addProvider(new BouncyCastleProvider()); logger.info("BC security provider added for EC algorithm"); } } public static void main(String[] args) { SpringApplication.run(JiraMspsUtilityApplication.class, args); } @Override public void run(String... args) throws IOException { System.out.println(String.format("\n2. Key Valut value stored in Azure Key Vault:\n%s\n",keyvault_value)); logger.info("\n2. Key Valut value stored in Azure Key Vault:\n{}\n",keyvault_value); if (args.length == 0) { loggerForWorkitemMetrics.info("There were no commandline arguments passed"); LoggerForSharedPlanUpdate.info("There were no commandline arguments passed"); System.exit(1); } else if (args.length > 1) { loggerForWorkitemMetrics.info("More than one argument passed"); LoggerForSharedPlanUpdate.info("More than one argument passed"); System.exit(1); } else { try { int arg = Integer.parseInt(args[0]); if (arg == 0) { boolean flag = jiraMspsService.updateWorkRequestMetricsInJira(); System.exit(flag ? 0 : 1); } else if (arg == 1) { boolean flag = jiraMspsService.updateSharedWorkplanEntries(); System.exit(flag ? 0 : 1); } else { loggerForWorkitemMetrics.info("Invalid argument(s) passed."); LoggerForSharedPlanUpdate.info("Invalid argument(s) passed."); System.exit(1); } } catch (NumberFormatException e) { loggerForWorkitemMetrics.info("Invalid numeric argument passed."); LoggerForSharedPlanUpdate.info("Invalid numeric argument passed."); System.exit(1); } } } }
内容的提问来源于stack exchange,提问作者r_k047
相关产品推荐
相关产品推荐

