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冲突引发的初始化异常
修复步骤
移除不必要的JwtDecoder注入
使用oauth2ResourceServer(jwt(Customizer.withDefaults()))时,Spring会自动查找容器中的JwtDecoderBean,手动注入反而会触发重复处理逻辑,直接删除注入代码即可。删除多余的httpBasic配置
资源服务器采用JWT认证,httpBasic配置完全冗余,会干扰认证流程,需要移除。调整配置类结构
简化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(); } }
- 验证RSA密钥配置
确保application.yml/properties中的密钥格式正确,示例:
rsa: public-key: "-----BEGIN PUBLIC KEY-----你的公钥内容-----END PUBLIC KEY-----" private-key: "-----BEGIN PRIVATE KEY-----你的私钥内容-----END PRIVATE KEY-----"
内容的提问来源于stack exchange,提问作者CODI
相关产品推荐
相关产品推荐

