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

SpringBoot从2.3.12升级到2.7.0时KeyVault配置启动报错

问题背景

现有SpringBoot应用原运行版本为2.3.12.RELEASE,因该版本存在安全漏洞需完成版本升级。实际升级至2.7.0、2.5.12、2.5.13等版本时,应用启动直接失败,核心报错为KeyVault属性源配置失败,完整错误栈如下:

java.lang.IllegalStateException: Failed to configure KeyVault property source
at com.microsoft.azure.keyvault.spring.KeyVaultEnvironmentPostProcessorHelper.addKeyVaultPropertySource(KeyVaultEnvironmentPostProcessorHelper.java:110)
at com.microsoft.azure.keyvault.spring.KeyVaultEnvironmentPostProcessor.postProcessEnvironment(KeyVaultEnvironmentPostProcessor.java:47)
at org.springframework.boot.env.EnvironmentPostProcessorApplicationListener.onApplicationEnvironmentPreparedEvent(EnvironmentPostProcessorApplicationListener.java:102)
at org.springframework.boot.env.EnvironmentPostProcessorApplicationListener.onApplicationEvent(EnvironmentPostProcessorApplicationListener.java:87)
at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:176)
at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:169)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:143)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:131)
at org.springframework.boot.context.event.EventPublishingRunListener.environmentPrepared(EventPublishingRunListener.java:85)
at org.springframework.boot.SpringApplicationRunListeners.lambda$environmentPrepared$2(SpringApplicationRunListeners.java:66)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:120)
at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:114)
at org.springframework.boot.SpringApplicationRunListeners.environmentPrepared(SpringApplicationRunListeners.java:65)
at org.springframework.boot.SpringApplication.prepareEnvironment(SpringApplication.java:344)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:302)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1295)
at com.honeywell.CaaS.GatewayManagerAPI.webapi.SpringBootMain.main(SpringBootMain.java:51)
Caused by: java.lang.RuntimeException: Max retries 3 times exceeded. Error Details: class java.util.Collections$SingletonList cannot be cast to class java.lang.String (java.util.Collections$SingletonList and java.lang.String are in module java.base of loader 'bootstrap')
at com.azure.core.http.policy.RetryPolicy.lambda$attemptAsync$1(RetryPolicy.java:127)
at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onError(FluxOnErrorResume.java:94)
at reactor.core.publisher.MonoFlatMap$FlatMapMain.onError(MonoFlatMap.java:172)
at reactor.core.publisher.MonoFlatMap$FlatMapMain.onError(MonoFlatMap.java:172)
at reactor.core.publisher.MonoFlatMap$FlatMapMain.secondError(MonoFlatMap.java:192)
at reactor.core.publisher.MonoFlatMap$FlatMapInner.onError(MonoFlatMap.java:259)
at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onError(MonoPeekTerminal.java:258)
at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onError(MonoPeekTerminal.java:258)
at reactor.core.publisher.FluxPeekFuseable$PeekFuseableConditionalSubscriber.onError(FluxPeekFuseable.java:553)
at reactor.core.publisher.FluxPeekFuseable$PeekFuseableConditionalSubscriber.onError(FluxPeekFuseable.java:553)
at reactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.onError(FluxMapFuseable.java:340)
at reactor.core.publisher.MonoCompletionStage.lambda$subscribe$0(MonoCompletionStage.java:77)
at java.base/java.util.concurrent.CompletableFuture.uniWhenComplete(CompletableFuture.java:859)
at java.base/java.util.concurrent.CompletableFuture$UniWhenComplete.tryFire(CompletableFuture.java:837)
at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:506)
at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1705)
at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.exec(CompletableFuture.java:1692)
at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:183)
Suppressed: java.lang.Exception: #block terminated with an error
    at reactor.core.publisher.BlockingSingleSubscriber.blockingGet(BlockingSingleSubscriber.java:99)
    at reactor.core.publisher.Mono.block(Mono.java:1707)
    at com.azure.security.keyvault.secrets.SecretClient.getSecretWithResponse(SecretClient.java:171)
    at com.azure.security.keyvault.secrets.SecretClient.getSecret(SecretClient.java:150)
    at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195)
    at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195)
    at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1655)
    at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
    at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
    at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913)
    at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578)
    at com.microsoft.azure.keyvault.spring.KeyVaultOperation.refreshProperties(KeyVaultOperation.java:139)
    at com.microsoft.azure.keyvault.spring.KeyVaultOperation.<init>(KeyVaultOperation.java:72)
    at com.microsoft.azure.keyvault.spring.KeyVaultEnvironmentPostProcessorHelper.addKeyVaultPropertySource(KeyVaultEnvironmentPostProcessorHelper.java:90)
    at com.microsoft.azure.keyvault.spring.KeyVaultEnvironmentPostProcessor.postProcessEnvironment(KeyVaultEnvironmentPostProcessor.java:47)
    at org.springframework.boot.env.EnvironmentPostProcessorApplicationListener.onApplicationEnvironmentPreparedEvent(EnvironmentPostProcessorApplicationListener.java:102)
    at org.springframework.boot.env.EnvironmentPostProcessorApplicationListener.onApplicationEvent(EnvironmentPostProcessorApplicationListener.java:87)
    at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:176)
    at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:169)
    at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:143)
    at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:131)
    at org.springframework.boot.context.event.EventPublishingRunListener.environmentPrepared(EventPublishingRunListener.java:85)
    at org.springframework.boot.SpringApplicationRunListeners.lambda$environmentPrepared$2(SpringApplicationRunListeners.java:66)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
    at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:120)
    at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:114)
    at org.springframework.boot.SpringApplicationRunListeners.environmentPrepared(SpringApplicationRunListeners.java:65)
    at org.springframework.boot.SpringApplication.prepareEnvironment(SpringApplication.java:344)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:302)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1295)
    at com.honeywell.CaaS.GatewayManagerAPI.webapi.SpringBootMain.main(SpringBootMain.java:51)
Caused by: java.lang.ClassCastException: class java.util.Collections$SingletonList cannot be cast to class java.lang.String (java.util.Collections$SingletonList and java.lang.String are in module java.base of loader 'bootstrap')
at com.nimbusds.oauth2.sdk.util.URLUtils.serializeParameters(URLUtils.java:105)
at com.microsoft.aad.msal4j.TokenRequestExecutor.createOauthHttpRequest(TokenRequestExecutor.java:51)
at com.microsoft.aad.msal4j.TokenRequestExecutor.executeTokenRequest(TokenRequestExecutor.java:36)
at com.microsoft.aad.msal4j.AbstractClientApplicationBase.acquireTokenCommon(AbstractClientApplicationBase.java:117)
at com.microsoft.aad.msal4j.AcquireTokenByAuthorizationGrantSupplier.execute(AcquireTokenByAuthorizationGrantSupplier.java:63)
at com.microsoft.aad.msal4j.AuthenticationResultSupplier.get(AuthenticationResultSupplier.java:59)
at com.microsoft.aad.msal4j.AuthenticationResultSupplier.get(AuthenticationResultSupplier.java:17)
at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1700)
... 6 common frames omitted
根因分析

报错的核心触发点是最底层的类型转换异常:Collections$SingletonList无法强转为String,触发位置在Nimbus OAuth2 SDK的URLUtils.serializeParameters方法,本质是依赖版本不兼容:

  • SpringBoot 2.4及以上版本升级了内置依赖管理的nimbus-oauth2-oidc-sdk版本,新版本SDK调整了URLUtils.serializeParameters的入参格式,将参数Map的value类型从String改为List<String>
  • 旧版本的Azure KeyVault Spring Starter依赖的msal4j(微软AAD认证库)版本是按照老版本Nimbus SDK的API编写的,没有适配新的参数类型,在请求AAD获取KeyVault访问Token时触发类型强转错误,重试3次失败后就会抛出KeyVault配置失败的启动异常。
    该问题和SpringBoot小版本无关,只要SpringBoot版本≥2.4,且使用的Azure KeyVault Starter、msal4j版本未做适配,就会稳定复现。
排查思路
  • 优先提取错误栈最底层的根异常,不要被上层的KeyVault配置失败、重试超限等表象误导,先定位具体的代码报错点
  • 查看应用依赖树,确认类路径下nimbus-oauth2-oidc-sdk、msal4j、azure-keyvault-spring-boot-starter三个依赖的实际加载版本
  • 核对当前使用的Azure KeyVault Starter官方兼容矩阵,确认其支持的SpringBoot版本、Nimbus版本范围,和当前升级后的SpringBoot依赖版本做比对
解决方案
  • 方案一(推荐):升级Azure KeyVault依赖到适配版本。将旧的com.microsoft.azure groupId下的老版KeyVault Starter,替换为官方后续维护的com.azure.spring:spring-cloud-azure-starter-keyvault-secrets,选择和目标SpringBoot版本匹配的依赖版本即可,新版本已经完成Nimbus SDK的API适配,不需要额外调整业务配置
  • 方案二:如果暂时无法替换KeyVault Starter,可在项目依赖管理中强制将msal4j版本升级到1.11.0及以上,该版本及以后已经适配了高版本Nimbus SDK的参数格式,改动量最小
  • 方案三(不推荐):在依赖管理中强制将nimbus-oauth2-oidc-sdk版本降级到SpringBoot 2.3.x默认携带的8.x版本。注意:该操作可能导致Spring Security OAuth2、Spring Cloud等依赖高版本Nimbus的组件出现兼容问题,非极端场景不要使用

内容的提问来源于stack exchange,提问作者Sugato

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 14:48:21