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

OAuth2资源服务器多租户下,如何为各JWT配置不同jwk-set-uri?

问题

我们的服务当前使用授权服务器S1颁发的JWT,且配置了与issuer分离的专属jwk-set-uri,application.yaml配置如下:

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          jwk-set-uri: original.server:8080/.well-known/jwks.json
          issuer-uri: https://s1.host.name

现需将授权服务器替换为S2,新颁发方也有独立的jwk-set-uri。为保证向后兼容,需同时支持新旧两种JWT格式(对应不同issuer和jwk-set-uri)。我期望能在application.yaml中配置多组JWT信息(issuer、jwk-set-uri等),示例如下:

spring:
  security:
    oauth2:
      resourceservers:
        server1: # 开发者自定义名称
          jwt:
            jwk-set-uri: original.jwks.server:8080/.well-known/jwks.json
            issuer-uri: https://s1.host.name
        server2:
          jwt:
            jwk-set-uri: new.jwks.server:8080/.well-known/jwks.json
            issuer-uri: https://s2.host.name

但目前Spring Security暂不支持该配置方式,仅支持单组jwk-set-uri与issuer配置。使用OAuth2资源服务器多租户的JwtIssuerReactiveAuthenticationManagerResolver时,也只能配置多个issuer,无法为每个issuer指定不同的jwk-set-uri,相关文档示例如下:

JwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerReactiveAuthenticationManagerResolver
    ("https://idp.example.org/issuerOne", "https://idp.example.org/issuerTwo");

http
    .authorizeExchange(exchanges -> exchanges
        .anyExchange().authenticated()
    )
    .oauth2ResourceServer(oauth2 -> oauth2
        .authenticationManagerResolver(authenticationManagerResolver)
    );

请问如何实现上述需求?

解决方案

1. 自定义配置类映射多组JWT配置

首先在application.yaml中添加自定义的多租户JWT配置(替代原有的单组spring.security.oauth2.resourceserver配置):

# 自定义多租户JWT配置
jwt:
  issuers:
    - name: server1
      issuer-uri: https://s1.host.name
      jwk-set-uri: original.jwks.server:8080/.well-known/jwks.json
    - name: server2
      issuer-uri: https://s2.host.name
      jwk-set-uri: new.jwks.server:8080/.well-known/jwks.json

然后创建配置类映射这些属性:

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import java.util.List;

@Configuration
@ConfigurationProperties(prefix = "jwt")
public class JwtIssuerProperties {
    private List<IssuerConfig> issuers;

    public List<IssuerConfig> getIssuers() {
        return issuers;
    }

    public void setIssuers(List<IssuerConfig> issuers) {
        this.issuers = issuers;
    }

    public static class IssuerConfig {
        private String name;
        private String issuerUri;
        private String jwkSetUri;

        // Getters and Setters
        public String getName() { return name; }
        public void setName(String name) { this.name = name; }
        public String getIssuerUri() { return issuerUri; }
        public void setIssuerUri(String issuerUri) { this.issuerUri = issuerUri; }
        public String getJwkSetUri() { return jwkSetUri; }
        public void setJwkSetUri(String jwkSetUri) { this.jwkSetUri = jwkSetUri; }
    }
}

2. 构建自定义的JwtIssuerReactiveAuthenticationManagerResolver

通过配置类为每个issuer创建专属的JwtDecoder和ReactiveAuthenticationManager,最终构建解析器:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider;
import org.springframework.security.oauth2.server.resource.authentication.JwtIssuerReactiveAuthenticationManagerResolver;
import reactor.core.publisher.Mono;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

@Configuration
public class MultiTenantJwtSecurityConfig {

    @Bean
    public JwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver(JwtIssuerProperties properties) {
        // 为每个issuer创建对应的认证管理器
        Map<String, ReactiveAuthenticationManager> authenticationManagers = properties.getIssuers().stream()
                .collect(Collectors.toMap(
                        JwtIssuerProperties.IssuerConfig::getIssuerUri,
                        this::createAuthenticationManager
                ));

        // 根据JWT的iss字段匹配对应认证管理器
        Function<String, Mono<ReactiveAuthenticationManager>> resolver = issuer ->
                Mono.justOrEmpty(authenticationManagers.get(issuer));

        return new JwtIssuerReactiveAuthenticationManagerResolver(resolver);
    }

    private ReactiveAuthenticationManager createAuthenticationManager(JwtIssuerProperties.IssuerConfig config) {
        // 创建指定jwk-set-uri的JwtDecoder
        JwtDecoder decoder = NimbusReactiveJwtDecoder.withJwkSetUri(config.getJwkSetUri()).build();
        // 绑定issuer校验逻辑
        decoder.setJwtValidator(jwt -> {
            if (!config.getIssuerUri().equals(jwt.getIssuer())) {
                throw new IllegalArgumentException("Invalid issuer");
            }
            return Mono.just(jwt);
        });
        JwtAuthenticationProvider provider = new JwtAuthenticationProvider(decoder);
        return provider::authenticate;
    }

    // 配置Spring Security资源服务器规则
    @Bean
    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http,
                                                           JwtIssuerReactiveAuthenticationManagerResolver resolver) {
        http
                .authorizeExchange(exchanges -> exchanges
                        .anyExchange().authenticated()
                )
                .oauth2ResourceServer(oauth2 -> oauth2
                        .authenticationManagerResolver(resolver)
                );
        return http.build();
    }
}

3. 核心逻辑说明

  • 每个issuer对应独立的JwtDecoder,确保使用专属的jwk-set-uri验证签名
  • 通过自定义解析逻辑,根据JWT的iss字段精准匹配对应的认证管理器
  • 完全兼容原有单租户验证逻辑,同时实现多issuer多jwk-set-uri的支持

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 20:40:36