client_credentials 未经 spring webclient OAuth2

问题描述

我正在开发 Spring Boot 应用程序,目前正在尝试调用使用 OAuth2 的服务。

应用程序是一个后端,正在调用一个后端。

我已经配置了一个 webclient bean 来处理获取令牌的调用,并在令牌过期时再次检索令牌。但我有一个例外:

2021-04-16 11:09:04.569 ERROR 7744 --- [nio-8080-exec-2] OAuth2AuthorizationRequestRedirectFilter : Authorization Request Failed: java.lang.IllegalArgumentException: Invalid Authorization Grant Type (client_credentials) for Client Registration with Id: drs

java.lang.IllegalArgumentException: Invalid Authorization Grant Type (client_credentials) for Client Registration with Id: drs
    at org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizationRequestResolver.resolve(DefaultOAuth2AuthorizationRequestResolver.java:142)
    at org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizationRequestResolver.resolve(DefaultOAuth2AuthorizationRequestResolver.java:89)
    at org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter.doFilterInternal(OAuth2AuthorizationRequestRedirectFilter.java:149)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.authentication.logout.logoutFilter.doFilter(logoutFilter.java:116)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:117)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:92)
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:77)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:358)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:271)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.boot.actuate.metrics.web.servlet.WebMvcmetricsFilter.doFilterInternal(WebMvcmetricsFilter.java:108)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:526)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:367)
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:860)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1591)
    at org.apache.tomcat.util.net.socketProcessorBase.run(SocketProcessorBase.java:49)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:748)

我对 bean 的配置:

@Configuration
public class OAuthEndpointClientConfiguration {

    @Bean
    public ReactiveClientRegistrationRepository clientRegistrations(
            @Value("${spring.security.oauth2.client.provider.drs.token-uri}") String tokenUri,@Value("${spring.security.oauth2.client.registration.drs.client-id}") String clientId,@Value("${spring.security.oauth2.client.registration.drs.client-secret}") String clientSecret,@Value("${spring.security.oauth2.client.registration.drs.scope}") String scope,@Value("${spring.security.oauth2.client.registration.drs.authorization-grant-type}") AuthorizationGrantType grantType,@Value("${spring.security.oauth2.client.registration.drs.client-authentication-method}") ClientAuthenticationMethod authenticationMethod
    ) {
        ClientRegistration clientRegistration = ClientRegistration
                .withRegistrationId("service")
                .tokenUri(tokenUri)
                .clientId(clientId)
                .clientSecret(clientSecret)
                .authorizationGrantType(grantType)
                .scope(scope)
                .build();
        return new InMemoryReactiveClientRegistrationRepository(clientRegistration);
    }

    @Bean
    public WebClient oAuthWebClient(ReactiveClientRegistrationRepository clientRegistrations) {
        ServerOAuth2AuthorizedClientExchangeFilterFunction oAuth = new ServerOAuth2AuthorizedClientExchangeFilterFunction(
                clientRegistrations,new UnAuthenticatedServerOAuth2AuthorizedClientRepository());
        oAuth.setDefaultClientRegistrationId("service");
        return WebClient.builder().filter(oAuth).build();
    }

}

我不得不配置一个 ReactiveClientRegistrationRepository 因为它没有在自动装配中找到。 我必须使用的授权类型是 client_credentials,但如上所述,它会引发异常。 查看产生异常的类(认类)时,我们可以看到如果授权类型与 AuthorizationGrantType.AUTHORIZATION_CODE 或 AuthorizationGrantType.IMPLICIT 不同,则会抛出异常:

package org.springframework.security.oauth2.client.web;

public final class DefaultOAuth2AuthorizationRequestResolver implements OAuth2AuthorizationRequestResolver {
    private static final String REGISTRATION_ID_URI_VARIABLE_NAME = "registrationId";
    private static final char PATH_DELIMITER = '/';
    private final ClientRegistrationRepository clientRegistrationRepository;
    private final AntPathRequestMatcher authorizationRequestMatcher;
    private final StringKeyGenerator stateGenerator = new Base64StringKeyGenerator(Base64.getUrlEncoder());
    private final StringKeyGenerator secureKeyGenerator = new Base64StringKeyGenerator(Base64.getUrlEncoder().withoutPadding(),96);

    public DefaultOAuth2AuthorizationRequestResolver(ClientRegistrationRepository clientRegistrationRepository,String authorizationRequestBaseUri) {
        Assert.notNull(clientRegistrationRepository,"clientRegistrationRepository cannot be null");
        Assert.hasText(authorizationRequestBaseUri,"authorizationRequestBaseUri cannot be empty");
        this.clientRegistrationRepository = clientRegistrationRepository;
        this.authorizationRequestMatcher = new AntPathRequestMatcher(authorizationRequestBaseUri + "/{" + "registrationId" + "}");
    }

(......)
    
    private OAuth2AuthorizationRequest resolve(HttpServletRequest request,String registrationId,String redirectUriAction) {
        if (registrationId == null) {
            return null;
        } else {
            ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(registrationId);
            if (clientRegistration == null) {
                throw new IllegalArgumentException("Invalid Client Registration with Id: " + registrationId);
            } else {
                Map<String,Object> attributes = new HashMap();
                attributes.put("registration_id",clientRegistration.getRegistrationId());
                Builder builder;
                if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(clientRegistration.getAuthorizationGrantType())) {
                    builder = OAuth2AuthorizationRequest.authorizationCode();
                    Map<String,Object> additionalParameters = new HashMap();
                    if (!CollectionUtils.isEmpty(clientRegistration.getScopes()) && clientRegistration.getScopes().contains("openid")) {
                        this.addNonceParameters(attributes,additionalParameters);
                    }

                    if (ClientAuthenticationMethod.NONE.equals(clientRegistration.getClientAuthenticationMethod())) {
                        this.addPkceParameters(attributes,additionalParameters);
                    }

                    builder.additionalParameters(additionalParameters);
                } else {
                    if (!AuthorizationGrantType.IMPLICIT.equals(clientRegistration.getAuthorizationGrantType())) {
                        throw new IllegalArgumentException("Invalid Authorization Grant Type (" + clientRegistration.getAuthorizationGrantType().getValue() + ") for Client Registration with Id: " + clientRegistration.getRegistrationId());
                    }

                    builder = OAuth2AuthorizationRequest.implicit();
                }

                String redirectUriStr = expandRedirectUri(request,clientRegistration,redirectUriAction);
                OAuth2AuthorizationRequest authorizationRequest = builder.clientId(clientRegistration.getClientId()).authorizationUri(clientRegistration.getProviderDetails().getAuthorizationUri()).redirectUri(redirectUriStr).scopes(clientRegistration.getScopes()).state(this.stateGenerator.generateKey()).attributes(attributes).build();
                return authorizationRequest;
            }
        }
    }
    
}

所以我有点卡住了,因为我必须使用 client_credentials 作为授权类型,但是这个类禁止我这样做。我猜我缺少一个配置,但我找不到什么。我找到的每个解决方案都只是解释了 Web 客户端 bean 的部分,并说它应该可以工作。

我什至不确定它是否应该通过解析器?我很难理解该解析器的用途是什么,似乎在应用程序本身想要使用 oAuth 时使用它。但这不是我的情况,我只想调用一个使用 oAuth 的应用程序。我有点失落。

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)