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

Spring Security JWT问题:无法继承最终类JwtAuthenticationProvider

问题描述

使用Spring Boot 3.2.5开发应用AppA,该应用依赖实现Spring Security OAuth2资源服务器(spring-boot-starter-oauth2-resource-server)的ModuleA模块,启动时出现如下错误:

Error creating bean with name
'org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration':
Unsatisfied dependency expressed through method 'setFilterChains'
parameter 0: Error creating bean with name
'oAuth2ResourceServerFilterChain' defined in class path resource
[com/modulea/oauth2resourceserver/config/Oauth2ResourceServerConfig.class]:
Failed to instantiate
[org.springframework.security.web.SecurityFilterChain]: Factory method
'oAuth2ResourceServerFilterChain' threw exception with message: Could
not postProcess
 org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider@26f1b53b of type class
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider

Caused by: org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'oAuth2ResourceServerFilterChain'
defined in class path resource
[oauth2resourceserver/config/Oauth2ResourceServerConfig.class]: Failed
to instantiate [org.springframework.security.web.SecurityFilterChain]: Factory method
'oAuth2ResourceServerFilterChain' threw exception with message: Could not postProcess

org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider@26f1b53b of type class
 org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider
    at
 org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:648)
~[spring-beans-6.1.6.jar:6.1.6]

项目结构

  • AppA的POM依赖:
<dependency>
  <groupId>com.modulea</groupId>
  <artifactId>oauth2-resourceserver</artifactId>
  <version>0.0.1-SNAPSHOT</version>
</dependency>
  • ModuleA目录结构:
src
└── main
    └── com
        └── modulea
            └── oauth2resourceserver
                └── config
                    ├── Oauth2ResourceServerConfig
                    ├── RsaKeyProperties
                    └── DecoderEncoderConfig

Oauth2ResourceServerConfig

package com.modulea.oauth2resourceserver.config;

import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;

import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;

@RequiredArgsConstructor(access = AccessLevel.PROTECTED, onConstructor_ = {@Autowired})
@Configuration
public class Oauth2ResourceServerConfig {
    private final JwtDecoder jwtDecoder;

    @Order(4)
    @Bean
    public SecurityFilterChain oAuth2ResourceServerFilterChain(HttpSecurity http) throws Exception {
        HttpSessionRequestCache requestCache = new HttpSessionRequestCache();
        requestCache.setMatchingRequestParameterName(null);
        http
                .csrf(AbstractHttpConfigurer::disable)
                .authorizeHttpRequests(authorize -> authorize
                        .requestMatchers("/internal/**")
                                .permitAll()
                        .anyRequest()
                                .authenticated())
                .oauth2ResourceServer(oAuth2ResourceServerConfigurer -> oAuth2ResourceServerConfigurer.jwt(Customizer.withDefaults()))
                .sessionManagement(sessionManagement -> sessionManagement
                        .sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .httpBasic(Customizer.withDefaults());
        return http
                .build();
    }
}

RsaKeyProperties

package com.modulea.oauth2resourceserver.config;

import org.springframework.boot.context.properties.ConfigurationProperties;

import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;

@ConfigurationProperties(prefix = "rsa")
public record RsaKeyProperties(RSAPublicKey publicKey, RSAPrivateKey privateKey) {
}

DecoderEncoderConfig

package com.modulea.oauth2resourceserver.config;

import com.nimbusds.jose.jwk.JWK;
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
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.JwtEncoder;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.security.oauth2.jwt.NimbusJwtEncoder;

@EnableConfigurationProperties({RsaKeyProperties.class})
@RequiredArgsConstructor
@Configuration
public class DecoderEncoderConfig {

    private final RsaKeyProperties rsaKeyProperties;

    @Bean
    public JwtDecoder jwtDecoder() {
        return NimbusJwtDecoder.withPublicKey(rsaKeyProperties.publicKey()).build();
    }

    @Bean
    public JwtEncoder jwtEncoder() {
        JWK jwk = new RSAKey.Builder(rsaKeyProperties.publicKey()).privateKey(rsaKeyProperties.privateKey()).build();
        JWKSource<SecurityContext> jwkSource = new ImmutableJWKSet<>(new JWKSet(jwk));
        return new NimbusJwtEncoder(jwkSource);
    }
}
解决方案

错误原因分析

核心问题是Spring Security在后置处理JwtAuthenticationProvider时失败,诱因包括:

  • 手动注入JwtDecoder导致Spring重复处理认证提供者
  • 多余的httpBasic配置干扰JWT认证流程
  • 配置顺序或Bean冲突引发的初始化异常

修复步骤

  1. 移除不必要的JwtDecoder注入
    使用oauth2ResourceServer(jwt(Customizer.withDefaults()))时,Spring会自动查找容器中的JwtDecoder Bean,手动注入反而会触发重复处理逻辑,直接删除注入代码即可。

  2. 删除多余的httpBasic配置
    资源服务器采用JWT认证,httpBasic配置完全冗余,会干扰认证流程,需要移除。

  3. 调整配置类结构
    简化Oauth2ResourceServerConfig的依赖,确保配置逻辑清晰。

修改后的Oauth2ResourceServerConfig

package com.modulea.oauth2resourceserver.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;

@Configuration
public class Oauth2ResourceServerConfig {

    @Order(4)
    @Bean
    public SecurityFilterChain oAuth2ResourceServerFilterChain(HttpSecurity http) throws Exception {
        HttpSessionRequestCache requestCache = new HttpSessionRequestCache();
        requestCache.setMatchingRequestParameterName(null);
        http
                .csrf(AbstractHttpConfigurer::disable)
                .authorizeHttpRequests(authorize -> authorize
                        .requestMatchers("/internal/**")
                                .permitAll()
                        .anyRequest()
                                .authenticated())
                .oauth2ResourceServer(oAuth2ResourceServerConfigurer -> oAuth2ResourceServerConfigurer.jwt(Customizer.withDefaults()))
                .sessionManagement(sessionManagement -> sessionManagement
                        .sessionCreationPolicy(SessionCreationPolicy.STATELESS));
        return http.build();
    }
}
  1. 验证RSA密钥配置
    确保application.yml/properties中的密钥格式正确,示例:
rsa:
  public-key: "-----BEGIN PUBLIC KEY-----你的公钥内容-----END PUBLIC KEY-----"
  private-key: "-----BEGIN PRIVATE KEY-----你的私钥内容-----END PRIVATE KEY-----"

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 20:54:53