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

在SecurityFilterChain中为不同URL模式配置不同JWT解码器

为不同URL模式配置独立JwtDecoder的优雅方案

方案一:使用多SecurityFilterChain(推荐)

Spring Security支持配置多个SecurityFilterChain Bean,通过@Order控制匹配优先级,每个链对应特定URL模式并独立配置JwtDecoder,完全复用oauth2ResourceServer原生功能。

1. 配置公开路径的FilterChain

@Bean
@Order(1)
SecurityFilterChain publicSecurityFilterChain(HttpSecurity http) throws Exception {
    http
        .securityMatcher("/health/check", "/swagger-ui/**", "/v3/api-docs/**")
        .authorizeHttpRequests(auth -> auth.anyRequest().permitAll())
        .cors(withDefaults());
    return http.build();
}

2. 配置/api/v2/pattern1路径的FilterChain(使用decoder1)

@Bean
@Order(2)
SecurityFilterChain pattern1SecurityFilterChain(HttpSecurity http, JwtDecoder decoder1) throws Exception {
    http
        .securityMatcher("/api/v2/pattern1/**")
        .authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
        .oauth2ResourceServer(oauth2 -> oauth2.jwt(jwt -> jwt.decoder(decoder1)))
        .cors(withDefaults());
    return http.build();
}

3. 配置其他/api/v2路径的FilterChain(使用decoder2)

@Bean
@Order(3)
SecurityFilterChain otherApiSecurityFilterChain(HttpSecurity http, JwtDecoder decoder2) throws Exception {
    http
        .securityMatcher("/api/v2/**")
        .authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
        .oauth2ResourceServer(oauth2 -> oauth2.jwt(jwt -> jwt.decoder(decoder2)))
        .cors(withDefaults());
    return http.build();
}

4. 定义两个独立的JwtDecoder Bean

@Bean
JwtDecoder decoder1(Environment env) {
    NimbusJwtDecoder jwtDecoder = (NimbusJwtDecoder) JwtDecoders.fromOidcIssuerLocation(env.getProperty("issuer.uri"));
    OAuth2TokenValidator<Jwt> withIssuer = JwtValidators.createDefaultWithIssuer(env.getProperty("issuer.uri"));
    OAuth2TokenValidator<Jwt> withAudience = new DelegatingOAuth2TokenValidator<>(withIssuer, audienceValidator("audience1"));
    jwtDecoder.setJwtValidator(withAudience);
    return jwtDecoder;
}

@Bean
JwtDecoder decoder2(Environment env) {
    NimbusJwtDecoder jwtDecoder = (NimbusJwtDecoder) JwtDecoders.fromOidcIssuerLocation(env.getProperty("issuer.uri"));
    OAuth2TokenValidator<Jwt> withIssuer = JwtValidators.createDefaultWithIssuer(env.getProperty("issuer.uri"));
    OAuth2TokenValidator<Jwt> withAudience = new DelegatingOAuth2TokenValidator<>(withIssuer, audienceValidator("audience2"));
    jwtDecoder.setJwtValidator(withAudience);
    return jwtDecoder;
}

// 自定义Audience验证器
private OAuth2TokenValidator<Jwt> audienceValidator(String expectedAudience) {
    return token -> token.getAudience().contains(expectedAudience)
            ? OAuth2TokenValidatorResult.success()
            : OAuth2TokenValidatorResult.failure(
                new OAuth2Error(OAuth2ErrorCodes.INVALID_TOKEN, "Invalid audience", null)
            );
}

方案优势:

  • 职责划分清晰,每个FilterChain专注处理特定路径的认证逻辑
  • 完全依赖Spring Security原生组件,无需手动实现JWT解析、认证流程
  • 通过@Order确保匹配顺序,避免路径拦截冲突

方案二:自定义JwtDecoder解析器

如果不想拆分多个FilterChain,可以实现一个动态选择解码器的JwtDecoder,根据请求路径切换对应的解码器。

1. 实现动态JwtDecoder

@Bean
JwtDecoder dynamicJwtDecoder(Environment env, JwtDecoder decoder1, JwtDecoder decoder2) {
    return token -> {
        // 获取当前请求上下文
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String path = request.getRequestURI();
        
        if (path.startsWith("/api/v2/pattern1")) {
            return decoder1.decode(token);
        } else if (path.startsWith("/api/v2")) {
            return decoder2.decode(token);
        }
        throw new JwtException("No valid decoder found for request path");
    };
}

2. 在原有SecurityFilterChain中使用该解码器

@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http, JwtDecoder dynamicJwtDecoder) throws Exception {
    http.authorizeHttpRequests(authorizeRequests ->
            authorizeRequests.requestMatchers("/health/check").permitAll()
                    .requestMatchers("swagger-ui/**", "/v3/api-docs/**").permitAll()
                    .requestMatchers("/api/v2/**").authenticated())
            .oauth2ResourceServer(oauth2 ->
                    oauth2.jwt(jwt -> jwt.decoder(dynamicJwtDecoder))
            )
            .cors(withDefaults());

    return http.build();
}

注意事项:

  • 依赖RequestContextHolder获取请求,需确保请求上下文在当前线程中可用
  • 逻辑相对集中,但不如多FilterChain方案清晰,适合简单场景

内容的提问来源于stack exchange,提问作者fireball.1

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 15:43:18