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

Spring资源服务器如何配置多个jwk-set-uri兼容Google/Apple登录

Spring Boot 2.4.2 资源服务器同时校验Google、Apple JWT令牌实现方案

Spring Boot默认的spring.security.oauth2.resourceserver.jwt.jwk-set-uri配置仅支持单个令牌颁发方,要同时支持多个来源的JWT校验,需要自定义多租户路由逻辑,按JWT自带的iss(颁发者)声明匹配对应的JWK校验源,具体实现步骤如下:


1. 清理默认单颁发方配置

注释或删除application.properties中原有的Google单JWK地址配置,避免自动配置覆盖自定义逻辑:

# 注释掉原有单节点配置
# spring.security.oauth2.resourceserver.jwt.jwk-set-uri = https://www.googleapis.com/oauth2/v3/certs

确保项目已经引入spring-boot-starter-oauth2-resource-server依赖,Spring Boot 2.4.2默认携带的Spring Security 5.4.x、Nimbus JOSE组件完全支持以下逻辑,无需额外引入第三方依赖。


2. 编写安全配置类,定义多颁发方JWT解码器

新增资源服务器安全配置类,实现按iss路由的JWT解码器,同时做解码器缓存避免重复拉取JWK集影响性能:

import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator;
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
import org.springframework.security.oauth2.jwt.*;
import org.springframework.security.web.SecurityFilterChain;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@EnableWebSecurity
public class ResourceServerSecurityConfig {
    // 定义两个信任的令牌颁发方标识与对应JWK集地址
    private static final String GOOGLE_ISSUER = "https://accounts.google.com";
    private static final String GOOGLE_JWK_SET_URI = "https://www.googleapis.com/oauth2/v3/certs";
    private static final String APPLE_ISSUER = "https://appleid.apple.com";
    private static final String APPLE_JWK_SET_URI = "https://appleid.apple.com/auth/keys";

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .authorizeRequests(auth -> auth
                        // 替换为自身业务的接口权限规则
                        .anyRequest().authenticated()
                )
                .oauth2ResourceServer(oauth2 -> oauth2
                        .jwt(jwt -> jwt.decoder(multiTenantJwtDecoder()))
                );
        return http.build();
    }

    @Bean
    public JwtDecoder multiTenantJwtDecoder() {
        // 缓存已初始化的颁发方解码器,避免重复请求JWK地址、重复初始化解码器
        Map<String, JwtDecoder> decoderCache = new ConcurrentHashMap<>();

        return token -> {
            // 无签名校验解析JWT,仅提取载荷中的iss字段做路由依据
            com.nimbusds.jwt.JWT parsedJwt = com.nimbusds.jwt.JWTParser.parse(token);
            String issuer = parsedJwt.getJWTClaimsSet().getIssuer();

            // 按iss匹配对应解码器,缓存无命中则初始化新的解码器
            JwtDecoder targetDecoder = decoderCache.computeIfAbsent(issuer, iss -> {
                JwtDecoder decoder;
                if (GOOGLE_ISSUER.equals(iss)) {
                    decoder = buildJwtDecoderForIssuer(GOOGLE_JWK_SET_URI, GOOGLE_ISSUER);
                } else if (APPLE_ISSUER.equals(iss)) {
                    decoder = buildJwtDecoderForIssuer(APPLE_JWK_SET_URI, APPLE_ISSUER);
                } else {
                    throw new JwtValidationException("不支持的令牌颁发来源", null);
                }
                return decoder;
            });

            // 用匹配到的解码器做完整校验:签名校验、过期时间校验、iss校验、aud校验
            return targetDecoder.decode(token);
        };
    }

    private JwtDecoder buildJwtDecoderForIssuer(String jwkSetUri, String expectedIssuer) {
        NimbusJwtDecoder decoder = NimbusJwtDecoder.withJwkSetUri(jwkSetUri).build();
        // 组合校验规则:默认时间校验 + 指定颁发方校验 + 自定义客户端ID(aud)校验
        OAuth2TokenValidator<Jwt> defaultValidator = JwtValidators.createDefaultWithIssuer(expectedIssuer);
        OAuth2TokenValidator<Jwt> audValidator = new JwtClaimValidator<>("aud", aud -> {
            // 替换为自身应用的Google客户端ID、Apple客户端ID/Service ID列表
            return aud.equals("你的Google客户端ID") || aud.equals("你的Apple客户端ID");
        });
        decoder.setJwtValidator(new DelegatingOAuth2TokenValidator<>(defaultValidator, audValidator));
        return decoder;
    }
}

3. 配置注意事项

  • 解码器缓存用ConcurrentHashMap实现,性能和单JWK配置方案无差异,不会出现每次请求都拉取公钥的问题
  • Apple返回的JWT的aud字段对应你在Apple开发者后台配置的Service ID(Web端)或iOS应用Bundle ID(App端),多个合法aud直接追加到校验逻辑里即可
  • 如果需要新增其他令牌颁发方,只需要在multiTenantJwtDecoder方法里新增对应iss和JWK地址的判断分支,不需要修改其他逻辑
  • 非信任颁发方的令牌会直接抛出校验异常,返回401未授权,符合资源服务器安全要求

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 16:51:27