Docker环境下Keycloak user_info端点令牌验证失败问题排查
从你的描述和错误日志来看,核心问题大概率是Token的Issuer(发行者)不匹配导致Keycloak验证token失败,进而出现USER_INFO_REQUEST_ERROR且clientId/userId为空的情况。结合Docker Compose的网络特性,我整理了几个关键的排查和解决方向:
1. 修复Token Issuer与Keycloak内部地址的匹配问题
当用户在浏览器端通过localhost:28080完成授权时,Keycloak颁发的Token里的iss字段值是http://localhost:28080/auth/realms/cinema(假设你的realm是cinema)。但网关在Docker内部调用Keycloak的user-info-uri时用的是Docker内部的服务地址(比如http://keycloak:8080),这时候Keycloak验证Token时会发现Token的iss和自己的内部地址不匹配,直接判定为invalid_token,就会出现你看到的日志。
解决步骤:
- 在Keycloak的Realm设置中,将Valid Redirect URIs和Web Origins同时配置外部访问地址(
http://localhost:*)和Docker内部网关地址(比如http://gateway:8890/*) - 调整Keycloak的启动参数,让它兼容外部和内部的Issuer验证。在docker-compose.yml中给Keycloak服务添加以下环境变量:
这样Keycloak会用environment: - KC_HOSTNAME=localhost - KC_HOSTNAME_PORT=28080 - KC_HOSTNAME_STRICT=false - KC_HOSTNAME_STRICT_HTTPS=false - KC_HTTP_ENABLED=truelocalhost:28080作为Token的Issuer,同时关闭严格的Issuer检查(KC_HOSTNAME_STRICT=false),允许内部服务用keycloak:8080来验证Token。
2. 统一Gateway OAuth2配置的内部服务地址
你的配置里resourceserver.jwt.jwk-set-uri使用了${app.config.keycloak.url}变量,这个变量在Docker环境下必须指向Keycloak的内部DNS名称(比如http://keycloak:8080),而不是localhost。如果这个变量还是指向localhost,网关在验证JWT时会无法访问Keycloak的证书端点,间接导致Token验证异常。
调整后的配置片段:
spring: security: oauth2: client: provider: keycloak-spring-gateway-client: token-uri: http://${KEYCLOAK:keycloak}:8080/auth/realms/${app.config.keycloak.realm}/protocol/openid-connect/token user-info-uri: http://${KEYCLOAK:keycloak}:8080/auth/realms/${app.config.keycloak.realm}/protocol/openid-connect/userinfo jwk-set-uri: http://${KEYCLOAK:keycloak}:8080/auth/realms/${app.config.keycloak.realm}/protocol/openid-connect/certs resourceserver: jwt: jwk-set-uri: http://${KEYCLOAK:keycloak}:8080/auth/realms/${app.config.keycloak.realm}/protocol/openid-connect/certs
3. 确认TokenRelay过滤器的配置正确性
TokenRelay过滤器负责将用户的Access Token转发到下游服务,但如果网关的OAuth2客户端配置存在问题,也可能导致Token传递异常:
- 确保
user-info-authentication-method: header配置正确,Keycloak默认支持通过Authorization头传递Token - 确认Keycloak中
gateway客户端的类型是confidential,并且配置中的client-secret与Keycloak里的完全一致
4. 验证Docker网络的连通性
虽然你提到用了DNS名称,但还是要确认网关容器能正常访问Keycloak容器:
- 进入网关容器,执行
curl http://keycloak:8080/auth/realms/cinema,检查是否能返回Realm的公开信息 - 如果无法访问,检查docker-compose中两个服务是否在同一个自定义网络下(默认的bridge网络也可以,但自定义网络更稳定)
内容的提问来源于stack exchange,提问作者pawel asdasdad

