Spring Boot 2.x升级至3.2.4时OAuth2客户端认证方法不兼容问题
Spring Boot 2.x升级至3.2.4后OAuth2客户端认证异常解决
问题现象
升级Spring Boot到3.2.4版本后,OAuth2客户端抛出如下异常:
java.lang.IllegalArgumentException: 此类默认支持`client_secret_basic`、`client_secret_post`和`none`认证方式。客户端[onboarding_service]正在使用[post]方式,请使用受支持的客户端认证方法,或使用`setRequestEntityConverter`提供支持[post]的实例。 at org.springframework.util.Assert.isTrue(Assert.java:129) ~[spring-core-6.1.5.jar:6.1.5] Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: Assembly trace from producer [reactor.core.publisher.MonoSubscribeOnCallable] : reactor.core.publisher.Mono.subscribeOn(Mono.java:4618) org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction.authorizeClient(ServletOAuth2AuthorizedClientExchangeFilterFunction.java:487) Error has been observed at the following site(s): *______Mono.subscribeOn ⇢ at org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction.authorizeClient(ServletOAuth2AuthorizedClientExchangeFilterFunction.java:487) *__________Mono.flatMap ⇢ at org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction.lambda$filter$14(ServletOAuth2AuthorizedClientExchangeFilterFunction.java:383) *____________Mono.defer ⇢ at org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction.filter(ServletOAuth2AuthorizedClientExchangeFilterFunction.java:380) *____Mono.switchIfEmpty ⇢ at org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction.filter(ServletOAuth2AuthorizedClientExchangeFilterFunction.java:379) |_ Mono.map ⇢ at org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction.filter(ServletOAuth2AuthorizedClientExchangeFilterFunction.java:386) |_ Mono.flatMap ⇢ at org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction.filter(ServletOAuth2AuthorizedClientExchangeFilterFunction.java:387) |_ Mono.switchIfEmpty ⇢ at org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction.filter(ServletOAuth2AuthorizedClientExchangeFilterFunction.java:388) |_ checkpoint ⇢ Request to GET https://onboarding-api.account- [DefaultWebClient] |_ Mono.switchIfEmpty ⇢ at org.springframework.web.reactive.function.client.DefaultWebClient$DefaultRequestBodyUriSpec.lambda$exchange$11(DefaultWebClient.java:461) |_ Mono.doOnNext ⇢ at org.springframework.web.reactive.function.client.DefaultWebClient$DefaultRequestBodyUriSpec.lambda$exchange$11(DefaultWebClient.java:467) |_ Mono.doOnError ⇢ at org.springframework.web.reactive.function.client.DefaultWebClient$DefaultRequestBodyUriSpec.lambda$exchange$11(DefaultWebClient.java:468) |_ Mono.doFinally ⇢ at org.springframework.web.reactive.function.client.DefaultWebClient$DefaultRequestBodyUriSpec.lambda$exchange$11(DefaultWebClient.java:469) |_ Mono.contextWrite ⇢ at org.springframework.web.reactive.function.client.DefaultWebClient$DefaultRequestBodyUriSpec.lambda$exchange$11(DefaultWebClient.java:475) *__Mono.deferContextual ⇢ at org.springframework.web.reactive.function.client.DefaultWebClient$DefaultRequestBodyUriSpec.exchange(DefaultWebClient.java:443) |_ Mono.flatMap ⇢ at org.springframework.web.reactive.function.client.DefaultWebClient$DefaultResponseSpec.bodyToMono(DefaultWebClient.java:572)
异常核心原因:客户端配置的post认证方式不在Spring Security OAuth2客户端默认支持列表中,官方默认仅支持client_secret_basic、client_secret_post和none三种规范认证方式。
解决方案
方案一:调整客户端认证方式配置
将配置中的简写post替换为官方标准的client_secret_post,这是最直接的修复方式。
示例application.yml配置修改:
spring: security: oauth2: client: registration: onboarding_service: client-authentication-method: client_secret_post
方案二:自定义请求实体转换器支持post简写
如果无法修改认证方式配置(比如依赖外部固定配置源),可以自定义转换器兼容post这种简写形式:
1. 实现自定义转换器
import org.springframework.security.oauth2.client.endpoint.OAuth2ClientCredentialsGrantRequest; import org.springframework.security.oauth2.client.endpoint.OAuth2ClientCredentialsGrantRequestEntityConverter; import org.springframework.security.oauth2.core.ClientAuthenticationMethod; import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.reactive.function.BodyInserters; public class CustomClientCredentialsGrantRequestEntityConverter extends OAuth2ClientCredentialsGrantRequestEntityConverter { @Override protected BodyInserters.FormInserter<String> populateTokenRequestBody(OAuth2ClientCredentialsGrantRequest grantRequest, BodyInserters.FormInserter<String> body) { ClientAuthenticationMethod clientAuthMethod = grantRequest.getClientRegistration().getClientAuthenticationMethod(); // 兼容简写的post认证方式 if (ClientAuthenticationMethod.POST.equals(clientAuthMethod) || "post".equals(clientAuthMethod.getValue())) { MultiValueMap<String, String> formParameters = new LinkedMultiValueMap<>(); formParameters.add(OAuth2ParameterNames.GRANT_TYPE, grantRequest.getGrantType().getValue()); formParameters.add(OAuth2ParameterNames.CLIENT_ID, grantRequest.getClientRegistration().getClientId()); formParameters.add(OAuth2ParameterNames.CLIENT_SECRET, grantRequest.getClientRegistration().getClientSecret()); return BodyInserters.fromFormData(formParameters); } return super.populateTokenRequestBody(grantRequest, body); } }
2. 配置WebClient时注册自定义转换器
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager; import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProvider; import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProviderBuilder; import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager; import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository; import org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction; import org.springframework.web.reactive.function.client.WebClient; @Configuration public class WebClientConfig { @Bean public WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) { ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Filter = new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager); // 设置自定义转换器 oauth2Filter.setRequestEntityConverter(new CustomClientCredentialsGrantRequestEntityConverter()); return WebClient.builder() .filter(oauth2Filter) .build(); } @Bean public OAuth2AuthorizedClientManager authorizedClientManager(ClientRegistrationRepository clientRegistrationRepository, OAuth2AuthorizedClientRepository authorizedClientRepository) { OAuth2AuthorizedClientProvider authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder() .clientCredentials() .build(); DefaultOAuth2AuthorizedClientManager authorizedClientManager = new DefaultOAuth2AuthorizedClientManager(clientRegistrationRepository, authorizedClientRepository); authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider); return authorizedClientManager; } }
内容的提问来源于stack exchange,提问作者Edgar Ayvazyan
相关产品推荐
相关产品推荐

