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

Spring Boot对接Keycloak验证JWT时出现SSL证书信任问题

Spring Boot响应式应用对接Keycloak(LetsEncrypt证书)JWT验证PKIX错误解决方案

问题背景

Spring Boot响应式应用与使用LetsEncrypt SSL证书的Keycloak服务器通信时,验证JWT出现Could not obtain the keys错误,根因是PKIX path building failed: unable to find valid certification path to requested target。已尝试将证书添加到$JAVA_HOME/lib/security/cacerts但未解决。

解决方案

1. 确认证书导入的是应用实际使用的JVM cacerts

很多时候应用运行的JVM并非系统的$JAVA_HOME(比如IDE内置JVM、容器内JVM),需先定位应用实际使用的JVM路径:

# Linux下查看应用进程的JVM路径
ps aux | grep java
# 或查看应用启动日志中的JVM路径信息

用keytool验证证书是否存在于对应JVM的cacerts中:

keytool -list -keystore /path/to/actual/jre/lib/security/cacerts -alias letsencryptroot
# 默认密码为changeit

若未找到,重新导入LetsEncrypt根证书(ISRG Root X1):

# 下载根证书
curl -O https://letsencrypt.org/certs/isrgrootx1.pem
# 导入到目标cacerts
keytool -importcert -file isrgrootx1.pem -alias letsencryptroot -keystore /path/to/actual/jre/lib/security/cacerts -storepass changeit -noprompt

2. 配置Spring Boot直接指定信任证书

无需依赖系统cacerts,直接在应用中配置信任证书:

  • 将isrgrootx1.pem放入项目src/main/resources目录
  • 修改application.yml添加SSL配置:
spring:
  ssl:
    bundle:
      jks:
        keycloak-truststore:
          source:
            type: PEM
            certificate: classpath:isrgrootx1.pem
  • 在ReactiveSecurityConfig中配置JWT解码器时指定信任存储:
@Bean
public ReactiveJwtDecoder reactiveJwtDecoder() throws Exception {
    SSLContext sslContext = SSLContexts.custom()
            .loadTrustMaterial(new ClassPathResource("isrgrootx1.pem"), (chain, authType) -> true)
            .build();

    HttpClient httpClient = HttpClient.create()
            .secure(sslSpec -> sslSpec.sslContext(sslContext));

    return NimbusReactiveJwtDecoder.withJwkSetUri("https://your-keycloak-domain/auth/realms/your-realm/protocol/openid-connect/certs")
            .httpClient(httpClient)
            .build();
}

3. 检查Keycloak证书链完整性

若Keycloak未返回完整证书链(仅返回叶子证书,缺少中间证书),也会导致PKIX错误:

# 检查Keycloak证书链
openssl s_client -connect your-keycloak-domain:443 -showcerts

若链不完整,需在Keycloak服务器上将叶子证书与中间证书合并为一个完整文件后重新配置。

4. 临时禁用SSL验证(仅测试环境)

生产环境绝对禁止,仅用于临时测试排查:

@Bean
public ReactiveJwtDecoder reactiveJwtDecoder() throws Exception {
    TrustManager[] trustAllCerts = new TrustManager[]{
            new X509TrustManager() {
                public X509Certificate[] getAcceptedIssuers() { return null; }
                public void checkClientTrusted(X509Certificate[] certs, String authType) {}
                public void checkServerTrusted(X509Certificate[] certs, String authType) {}
            }
    };

    SSLContext sslContext = SSLContext.getInstance("SSL");
    sslContext.init(null, trustAllCerts, new SecureRandom());

    HttpClient httpClient = HttpClient.create()
            .secure(sslSpec -> sslSpec.sslContext(sslContext)
                    .defaultConfiguration(defaultConfig -> defaultConfig
                            .handlerConfigurator(handler -> handler.getSslEngineConfigurator().setHostnameVerifier((s, sslSession) -> true))));

    return NimbusReactiveJwtDecoder.withJwkSetUri("https://your-keycloak-domain/auth/realms/your-realm/protocol/openid-connect/certs")
            .httpClient(httpClient)
            .build();
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 04:50:11