Docker Compose环境下Keycloak令牌颁发者不匹配问题求解
解决Keycloak令牌颁发者(Issuer)内外网不匹配问题
在Docker Compose环境中,Keycloak对外暴露端口供外部应用(主机上的Spring MVC)访问,对内则通过容器名供同网络的资源服务器访问,导致令牌中的Issuer是外部地址(http://localhost:8042/auth/realms/test),但资源服务器只能访问内部地址(http://keycloak:8080/auth/realms/test),引发验证失败。以下是几种可行的解决方法:
方案一:资源服务器端自定义Issuer转换验证
通过Spring Security OAuth2的自定义JwtDecoder,将令牌中的外部Issuer替换为资源服务器可访问的内部地址后再进行验证,无需修改Keycloak配置。
代码示例(替换Issuer):
import org.springframework.security.oauth2.jwt.*; import reactor.core.publisher.Mono; @Bean public JwtDecoder jwtDecoder() { // 资源服务器内部访问Keycloak的JWKS地址 String internalJwksUri = "http://keycloak:8080/auth/realms/test/protocol/openid-connect/certs"; String expectedInternalIssuer = "http://keycloak:8080/auth/realms/test"; String tokenExternalIssuer = "http://localhost:8042/auth/realms/test"; NimbusJwtDecoder originalDecoder = NimbusJwtDecoder.withJwkSetUri(internalJwksUri).build(); // 自定义解码逻辑:替换令牌中的Issuer为内部地址 return jwtToken -> { Jwt decodedJwt = originalDecoder.decode(jwtToken); if (tokenExternalIssuer.equals(decodedJwt.getIssuer())) { // 生成修改Issuer的新JWT对象 return Mono.just(Jwt.withTokenValue(jwtToken) .headers(decodedJwt.getHeaders()) .claims(claims -> claims.put("iss", expectedInternalIssuer)) .build()); } return Mono.just(decodedJwt); }; }
代码示例(允许双Issuer):
如果不想修改令牌内容,也可以直接允许两个合法的Issuer:
import org.springframework.security.oauth2.jwt.*; import reactor.core.publisher.Mono; @Bean public JwtDecoder jwtDecoder() { String internalIssuer = "http://keycloak:8080/auth/realms/test"; String externalIssuer = "http://localhost:8042/auth/realms/test"; NimbusJwtDecoder decoder = NimbusJwtDecoder.withJwkSetUri(internalIssuer + "/protocol/openid-connect/certs").build(); // 自定义验证器,允许两个合法的Issuer JwtValidator validator = JwtValidators.createDefaultWithIssuer(internalIssuer) .and(jwt -> { String issuer = jwt.getIssuer(); if (internalIssuer.equals(issuer) || externalIssuer.equals(issuer)) { return Mono.just(jwt); } return Mono.error(new InvalidIssuerException("无效的颁发者: " + issuer)); }); decoder.setJwtValidator(validator); return decoder; }
方案二:配置Keycloak支持多Issuer(旧版JBoss镜像)
对于JBoss版本的Keycloak,可以通过启动参数添加允许的Issuer列表,让Keycloak颁发的令牌同时被内外网地址验证。
Docker Compose配置修改:
services: keycloak: image: jboss/keycloak environment: - KEYCLOAK_USER=admin - KEYCLOAK_PASSWORD=admin - PROXY_ADDRESS_FORWARDING=true command: > -Dkeycloak.allowed.issuers=http://localhost:8042/auth/realms/test,http://keycloak:8080/auth/realms/test ports: - "8042:8080" networks: - your-network
之后在资源服务器中使用方案一的双Issuer验证逻辑即可。
方案三:开发环境修改本地Hosts(快速临时解决)
仅适用于开发环境:在本地主机的hosts文件中添加映射,让keycloak域名指向本地回环地址:
127.0.0.1 keycloak
然后修改Keycloak的环境变量,设置前端URL为外部可访问的容器域名:
services: keycloak: image: jboss/keycloak environment: - KEYCLOAK_USER=admin - KEYCLOAK_PASSWORD=admin - KEYCLOAK_FRONTEND_URL=http://keycloak:8042/auth ports: - "8042:8080" networks: - your-network
此时令牌中的Issuer会是http://keycloak:8042/auth/realms/test,资源服务器内部可通过Docker DNS解析keycloak域名,再配合方案一的双Issuer验证逻辑即可完成验证。
内容的提问来源于stack exchange,提问作者tierriminator
相关产品推荐
相关产品推荐

