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

Spring Security集成Keycloak时发行者URI不匹配问题求解

Spring Security与Keycloak集成:Issuer URI不匹配问题解决

问题背景

我正在使用Spring Security与Keycloak集成,配置文件相关属性:

spring.security.oauth2.client.provider.keycloak.issuer-uri = http://a.com/auth/realms/arealm

协议为http。

Keycloak公网地址是https://a.com/auth/realms/arealm,协议为https。

Keycloak启动参数:

command:
  - start
  - --optimized
  - --http-enabled=true
  - --proxy-headers
  - forwarded
  - --hostname
  - https://a.com/auth/
  - --hostname-backchannel-dynamic=true

部署架构:Keycloak与应用同属局域网,HTTPS由前端负载均衡器终止。应用内调用以下URL可正常响应:

curl http://a.com/auth/realms/arealm/.well-known/openid-configuration

但Spring Security报错:

The Issuer https://a.com/auth/realms/arealm provided in the configuration metadata did not match the requested issuer http://a.com/auth/realms/arealm

不想把Spring配置改成https(会迫使应用走公网访问Keycloak,曾用/etc/hosts映射但方案不优雅),求解决发行者URI不匹配的方法。


解决方案

方案1:调整Keycloak后端通道hostname配置

修改Keycloak启动参数,指定后端通道使用局域网内的http地址作为hostname,这样Keycloak返回的OpenID元数据中issuer会和Spring配置的http地址一致:

command:
  - start
  - --optimized
  - --http-enabled=true
  - --proxy-headers
  - forwarded
  - --hostname
  - https://a.com/auth/
  # 替换动态后端hostname为固定http地址
  - --hostname-backchannel=http://a.com/auth/

此方案无需修改Spring代码,仅调整Keycloak配置,是最优雅的解决方案,直接从源头解决了元数据中issuer不匹配的问题。

方案2:自定义JWT Issuer验证器

通过自定义验证逻辑,允许Spring Security接受http和https两种版本的issuer:

  1. 编写自定义验证器类:
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;
import org.springframework.security.oauth2.jwt.Jwt;

public class FlexibleIssuerValidator implements OAuth2TokenValidator<Jwt> {
    private final String baseIssuer;

    public FlexibleIssuerValidator(String baseIssuer) {
        this.baseIssuer = baseIssuer;
    }

    @Override
    public OAuth2TokenValidatorResult validate(Jwt token) {
        String tokenIssuer = token.getIssuer();
        // 匹配http/https两种协议的issuer
        if (baseIssuer.equals(tokenIssuer) ||
            baseIssuer.replace("http://", "https://").equals(tokenIssuer) ||
            baseIssuer.replace("https://", "http://").equals(tokenIssuer)) {
            return OAuth2TokenValidatorResult.success();
        }
        return OAuth2TokenValidatorResult.failure(
            new OAuth2Error("invalid_issuer", "Issuer does not match expected value", null)
        );
    }
}
  1. 在Security配置中注册该验证器:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.JwtDecoders;
import org.springframework.security.oauth2.jwt.JwtValidators;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;

@Configuration
public class SecurityConfig {

    @Bean
    public JwtDecoder jwtDecoder() {
        String issuerUri = "http://a.com/auth/realms/arealm";
        NimbusJwtDecoder decoder = (NimbusJwtDecoder) JwtDecoders.fromIssuerLocation(issuerUri);
        
        // 组合默认验证器与自定义验证器
        decoder.setJwtValidator(
            JwtValidators.createDefaultWithIssuer(issuerUri)
                .and(new FlexibleIssuerValidator(issuerUri))
        );
        return decoder;
    }
}

方案3:修改OpenID元数据中的Issuer值

通过Spring的OAuth2ClientPropertiesCustomizer,在加载Keycloak元数据时替换issuer为配置的http地址:

import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties;
import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientPropertiesCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class OAuth2Config {

    @Bean
    public OAuth2ClientPropertiesCustomizer clientPropertiesCustomizer() {
        return properties -> {
            // 强制替换Keycloak provider元数据中的issuer为http地址
            properties.getProvider()
                .get("keycloak")
                .getMetadata()
                .put("issuer", "http://a.com/auth/realms/arealm");
        };
    }
}

内容的提问来源于stack exchange,提问作者uylmz

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 20:58:14