Docker Compose下SpringBoot对接Keycloak颁发者不匹配问题排查
问题现象
启动服务时抛出以下错误:
Caused by: java.lang.IllegalStateException: The Issuer "http://localhost:8080/realms/RepitRealm" provided in the configuration metadata did not match the requested issuer "http://keycloak:8080/realms/RepitRealm"
相关配置
1. docker-compose.yml关键片段
networks: local-keycloak: services: keycloak: build: ./keycloak/ container_name: repit-keycloak ports: - "8881:8080" - "8443:8443" volumes: - ./keycloak/data:/opt/keycloak/data/import environment: KEYCLOAK_ADMIN: "admin" KEYCLOAK_ADMIN_PASSWORD: "admin" # KC_HOSTNAME: "keycloak" networks: - local-keycloak depends_on: - keycloak-db restart: unless-stopped command: - start-dev - --import-realm backend: image: [...] container_name: repit-backend environment: spring_profiles_active: "external" KEYCLOAK_URL: "http://keycloak:8080/realms/RepitRealm" APPLICATION_PORT: "8880" ports: - "8880:8880" networks: - local-keycloak depends_on: - keycloak restart: unless-stopped
2. Keycloak Dockerfile内容
FROM quay.io/keycloak/keycloak:latest as builder # Enable health and metrics support ENV KC_HEALTH_ENABLED=true ENV KC_METRICS_ENABLED=true # Configure a database vendor ENV KC_DB=postgres WORKDIR /opt/keycloak # for demonstration purposes only, please make sure to use proper certificates in production instead RUN keytool -genkeypair -storepass password -storetype PKCS12 -keyalg RSA -keysize 2048 -dname "CN=server" -alias server -ext "SAN:c=DNS:localhost,IP:127.0.0.1" -keystore conf/server.keystore RUN /opt/keycloak/bin/kc.sh build FROM quay.io/keycloak/keycloak:latest COPY --from=builder /opt/keycloak/ /opt/keycloak/ # change these values to point to a running postgres instance ENV KC_DB=postgres ENV KC_DB_URL=jdbc:postgresql://keycloak-db:5432/keycloak ENV KC_DB_USERNAME=keycloak ENV KC_DB_PASSWORD=password ENV KC_HOSTNAME=localhost ENTRYPOINT ["/opt/keycloak/bin/kc.sh"]
3. SpringBoot "external"配置文件关键片段
spring.security.oauth2.client.registration.keycloak.client-id=repit spring.security.oauth2.client.registration.keycloak.authorization-grant-type=authorization_code spring.security.oauth2.client.registration.keycloak.scope=openid spring.security.oauth2.client.provider.keycloak.issuer-uri=${KEYCLOAK_URL} spring.security.oauth2.client.provider.keycloak.user-name-attribute=preferred_username spring.security.oauth2.resourceserver.jwt.issuer-uri=${KEYCLOAK_URL}
注:直接在IDE启动后端时运行正常;将Keycloak的
KC_HOSTNAME改为keycloak后服务可启动,但前端认证会被重定向到http://keycloak:8881/realms/RepitRealm。
问题原因
核心矛盾在于Keycloak返回的Issuer地址和SpringBoot请求的地址不一致:
- SpringBoot容器通过Docker内部服务名
keycloak请求Keycloak,配置的Issuer是http://keycloak:8080/realms/RepitRealm - Keycloak因Dockerfile中
KC_HOSTNAME=localhost的配置,返回的Issuer是http://localhost:8080/realms/RepitRealm - SpringBoot的OAuth2客户端会严格校验Issuer一致性,导致启动失败
解决方案
方案1:本地开发环境适配(同时支持容器内通信和外部浏览器访问)
该方案让Keycloak同时兼容内部服务名和外部localhost访问,解决容器通信和前端重定向问题:
修改docker-compose.yml中Keycloak的环境变量
environment: KEYCLOAK_ADMIN: "admin" KEYCLOAK_ADMIN_PASSWORD: "admin" KC_HOSTNAME: localhost KC_HOSTNAME_STRICT: false KC_HOSTNAME_STRICT_HTTPS: false KC_HTTP_ENABLED: true KC_PROXY: edgeKC_HOSTNAME_STRICT: false:允许Keycloak接受不同主机名的请求KC_PROXY: edge:让Keycloak从请求头中获取真实访问地址,适配端口映射场景- 保留
KC_HOSTNAME=localhost确保前端访问时重定向地址正确
配置本地hosts映射
在本地机器的hosts文件中添加:127.0.0.1 keycloak这样前端访问
http://keycloak:8881/realms/RepitRealm会解析到本地,避免无法访问的问题保持SpringBoot配置不变
继续使用KEYCLOAK_URL: "http://keycloak:8080/realms/RepitRealm",容器内通过服务名通信性能最优
方案2:统一访问地址(适合测试/生产)
如果不需要同时支持两种访问方式,可统一用localhost作为访问入口:
修改SpringBoot的环境变量
将KEYCLOAK_URL改为http://localhost:8080/realms/RepitRealm确保Keycloak配置正确
保持KC_HOSTNAME=localhost,容器端口映射8881:8080不变容器访问宿主机适配
若SpringBoot容器无法直接访问localhost,可替换为host.docker.internal(Docker内置的宿主机别名,Windows/macOS默认支持,Linux需额外配置)
验证方法
启动服务后,访问http://localhost:8881/realms/RepitRealm/.well-known/openid-configuration,查看返回的issuer字段是否与SpringBoot配置的一致。一致则说明配置正确。
内容的提问来源于stack exchange,提问作者Romain Hardenne

