Docker容器中Spring Boot应用的Keycloak令牌验证失败问题
问题背景
Spring Boot本地运行时Keycloak令牌验证完全正常,但部署到Docker容器后就失效。已尝试设置Keycloak realm的frontendUrl方案无效,环境情况:
- Spring Boot+数据库、Keycloak分别通过两个独立Docker Compose运行,共用同一个外部网络
- 本地可通过
localhost:8180访问Keycloak,localhost:8080访问应用,且Spring Boot容器能ping通Keycloak容器 - 无论是否设置
frontendUrl,将应用的issuer-uri改为Keycloak容器名地址后,验证依然失败;即使令牌中的iss值与应用配置完全一致,容器内的应用仍无法完成验证
具体排查与解决步骤
1. 先抓准具体错误日志
Docker中启动Spring Boot时,临时调高日志级别,获取OAuth2验证的详细错误:
在application.yml中添加:
logging: level: org.springframework.security: DEBUG org.springframework.web: DEBUG
重点关注是否出现Unable to resolve the JWKS endpoint(JWKS端点无法访问)、Invalid issuer(签发者不匹配)这类日志,这是定位问题的关键。
2. 用Keycloak hostname配置替代frontendUrl(Keycloak 17+版本)
新版本Keycloak已不再推荐frontendUrl,改用hostname系列参数配置内外网地址,在Keycloak的Docker Compose中添加环境变量:
environment: KC_HOSTNAME: <keycloak-container-name> KC_HOSTNAME_PORT: <keycloak容器内部端口> KC_HOSTNAME_STRICT: false KC_HOSTNAME_STRICT_HTTPS: false
如果Keycloak启动命令用的是kc.sh start,还需加上参数--hostname-strict=false,避免严格校验访问地址。
3. 确保issuer-uri使用容器内部可访问的完整地址
容器间通信必须用Keycloak容器的内部地址+内部端口,而非本地映射的外部端口。比如:
- 如果Keycloak Docker启动时端口映射是
ports: - "8180:8080",说明容器内部端口是8080,那么应用的配置应该是:
spring: security: oauth2: resourceserver: jwt: issuer-uri: http://<keycloak-container-name>:8080/realms/<realmname>
注意:本地运行时用localhost:8180,Docker运行时必须切换为容器内部地址,两者不能混用。
4. 验证Spring Boot容器能访问Keycloak的JWKS端点
进入Spring Boot容器,执行curl命令测试Keycloak的密钥接口:
curl http://<keycloak-container-name>:<内部端口>/realms/<realmname>/protocol/openid-connect/certs
如果能返回JSON格式的密钥列表,说明网络和端点正常;如果超时或报错,检查外部网络是否正确创建、容器是否加入同一网络。
5. 核对令牌iss字段与应用配置的一致性
用jwt.io解析你从Postman获取的令牌,查看iss字段的值是否和应用配置的issuer-uri完全一致(包括协议、域名/容器名、端口、路径)。如果不一致,调整Keycloak的hostname配置或应用的issuer-uri,确保两者完全匹配。
内容的提问来源于stack exchange,提问作者naptoon

