You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 16:01:32