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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 20:01:07