当前位置: 首页 > 知识库问答 >
问题:

SpringWebFlux:redirectUriTemplate的断言不为null,即使我使用的是OAuth2.0客户端凭据

闾丘文昌
2023-03-14

我将Spring Boot 2.2.4与Spring Boot starter webfluxSpring Boot starter securitySpring-security-oauth2-client一起使用。

使用reactiveWebClient,我想使用OAuth 2.0客户端凭据调用Auth0的管理API来获取一些用户信息。

我已经学习了Baeldung的SpringWebClient和OAuth2支持教程。我的应用程序。属性如下所示:

spring.security.oauth2.client.registration.auth0.authorization-grant-type=client_credentials
spring.security.oauth2.client.registration.auth0.client-id=XXX
spring.security.oauth2.client.registration.auth0.client-secret=YYY
spring.security.oauth2.client.provider.auth0.token-uri=https://ZZZ.eu.auth0.com/oauth/token

我的Spring@配置如下:

@Configuration
@RequiredArgsConstructor
class Auth0Configuration {

    @Value("${auth0.api.base-url}")
    private final String auth0ApiBaseUrl;

    @Bean
    WebClient auth0WebClient(ReactiveClientRegistrationRepository reactiveClientRegistrationRepository) {
        ServerOAuth2AuthorizedClientExchangeFilterFunction oauth =
                new ServerOAuth2AuthorizedClientExchangeFilterFunction(
                        reactiveClientRegistrationRepository,
                        new UnAuthenticatedServerOAuth2AuthorizedClientRepository());
        oauth.setDefaultClientRegistrationId("auth0");
        return WebClient.builder()
                .filter(oauth)
                .baseUrl(auth0ApiBaseUrl)
                .build();
    }

}

但是,当我使用WebClient进行第一次调用时,会发生以下错误:

2020-02-12 20:18:11.098 ERROR 1945 --- [ctor-http-nio-3] a.w.r.e.AbstractErrorWebExceptionHandler : [f0218135]  500 Server Error for HTTP GET "/oauth2/authorization/auth0"

java.lang.IllegalArgumentException: URI must not be null
    at org.springframework.util.Assert.notNull(Assert.java:198)
    Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Error has been observed at the following site(s):
    |_ checkpoint ⇢ org.springframework.security.oauth2.client.web.server.OAuth2AuthorizationRequestRedirectWebFilter [DefaultWebFilterChain]
    |_ checkpoint ⇢ org.springframework.security.web.server.context.ReactorContextWebFilter [DefaultWebFilterChain]
    |_ checkpoint ⇢ org.springframework.security.web.server.csrf.CsrfWebFilter [DefaultWebFilterChain]
    |_ checkpoint ⇢ org.springframework.security.web.server.header.HttpHeaderWriterWebFilter [DefaultWebFilterChain]
    |_ checkpoint ⇢ org.springframework.security.config.web.server.ServerHttpSecurity$ServerWebExchangeReactorContextWebFilter [DefaultWebFilterChain]
    |_ checkpoint ⇢ org.springframework.security.web.server.WebFilterChainProxy [DefaultWebFilterChain]
    |_ checkpoint ⇢ org.springframework.boot.actuate.metrics.web.reactive.server.MetricsWebFilter [DefaultWebFilterChain]
    |_ checkpoint ⇢ HTTP GET "/oauth2/authorization/auth0" [ExceptionHandlingWebHandler]
Stack trace:
        at org.springframework.util.Assert.notNull(Assert.java:198)
        at org.springframework.web.util.UriComponentsBuilder.fromUriString(UriComponentsBuilder.java:212)
        at org.springframework.security.oauth2.client.web.server.DefaultServerOAuth2AuthorizationRequestResolver.expandRedirectUri(DefaultServerOAuth2AuthorizationRequestResolver.java:214)
        at org.springframework.security.oauth2.client.web.server.DefaultServerOAuth2AuthorizationRequestResolver.authorizationRequest(DefaultServerOAuth2AuthorizationRequestResolver.java:131)
        at org.springframework.security.oauth2.client.web.server.DefaultServerOAuth2AuthorizationRequestResolver.lambda$resolve$3(DefaultServerOAuth2AuthorizationRequestResolver.java:121)
        at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:100)
        at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:67)
        at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2199)
        at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.set(Operators.java:2007)
        at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onSubscribe(Operators.java:1881)
        at reactor.core.publisher.MonoJust.subscribe(MonoJust.java:54)
        at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:55)
        at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:150)
        at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:121)
        at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:121)
        at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:121)
        at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.onNext(FluxFilterFuseable.java:112)
        at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2199)
        at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.request(FluxFilterFuseable.java:184)
        at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.request(FluxMapFuseable.java:162)
        at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.request(FluxMapFuseable.java:162)
        at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.request(FluxMapFuseable.java:162)
        at reactor.core.publisher.MonoFlatMap$FlatMapMain.onSubscribe(MonoFlatMap.java:103)
        at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onSubscribe(FluxMapFuseable.java:90)
        at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onSubscribe(FluxMapFuseable.java:90)
        at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onSubscribe(FluxMapFuseable.java:90)
        at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.onSubscribe(FluxFilterFuseable.java:81)
        at reactor.core.publisher.MonoJust.subscribe(MonoJust.java:54)
        at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:55)
        at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)
        at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:55)
        at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)
        at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)
        at reactor.core.publisher.Mono.subscribe(Mono.java:4105)
        at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.drain(MonoIgnoreThen.java:172)
        at reactor.core.publisher.MonoIgnoreThen.subscribe(MonoIgnoreThen.java:56)
        at reactor.core.publisher.Mono.subscribe(Mono.java:4105)
        at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onComplete(FluxSwitchIfEmpty.java:75)
        at reactor.core.publisher.MonoFlatMap$FlatMapMain.onComplete(MonoFlatMap.java:174)
        at reactor.core.publisher.MonoFlatMap$FlatMapMain.onComplete(MonoFlatMap.java:174)
        at reactor.core.publisher.FluxFilter$FilterSubscriber.onComplete(FluxFilter.java:160)
        at reactor.core.publisher.FluxFilter$FilterConditionalSubscriber.onComplete(FluxFilter.java:293)
        at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onComplete(FluxSwitchIfEmpty.java:78)
        at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1638)
        at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:144)
        at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.onNext(FluxFilterFuseable.java:112)
        at reactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.onNext(FluxMapFuseable.java:287)
        at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2199)
        at reactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.request(FluxMapFuseable.java:346)
        at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.request(FluxFilterFuseable.java:184)
        at reactor.core.publisher.MonoFlatMap$FlatMapMain.onSubscribe(MonoFlatMap.java:103)
        at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.onSubscribe(FluxFilterFuseable.java:81)
        at reactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.onSubscribe(FluxMapFuseable.java:255)
        at reactor.core.publisher.MonoJust.subscribe(MonoJust.java:54)
        at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:55)
        at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)
        at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:55)
        at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)
        at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:55)
        at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)
        at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)
        at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:150)
        at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:121)
        at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:121)
        at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1637)
        at reactor.core.publisher.MonoFlatMap$FlatMapInner.onNext(MonoFlatMap.java:241)
        at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1637)
        at reactor.core.publisher.MonoCollectList$MonoCollectListSubscriber.onComplete(MonoCollectList.java:121)
        at reactor.core.publisher.FluxIterable$IterableSubscription.fastPath(FluxIterable.java:333)
        at reactor.core.publisher.FluxIterable$IterableSubscription.request(FluxIterable.java:198)
        at reactor.core.publisher.MonoCollectList$MonoCollectListSubscriber.onSubscribe(MonoCollectList.java:72)
        at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:139)
        at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:63)
        at reactor.core.publisher.MonoFromFluxOperator.subscribe(MonoFromFluxOperator.java:72)
        at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:150)
        at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:67)
        at reactor.core.publisher.MonoNext$NextSubscriber.onNext(MonoNext.java:76)
        at reactor.core.publisher.FluxFilterWhen$FluxFilterWhenSubscriber.drain(FluxFilterWhen.java:295)
        at reactor.core.publisher.FluxFilterWhen$FluxFilterWhenSubscriber.onNext(FluxFilterWhen.java:134)
        at reactor.core.publisher.FluxIterable$IterableSubscription.slowPath(FluxIterable.java:243)
        at reactor.core.publisher.FluxIterable$IterableSubscription.request(FluxIterable.java:201)
        at reactor.core.publisher.FluxFilterWhen$FluxFilterWhenSubscriber.onSubscribe(FluxFilterWhen.java:194)
        at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:139)
        at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:63)
        at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:55)
        at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)
        at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:55)
        at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)
        at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:55)
        at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)
        at reactor.core.publisher.Mono.subscribe(Mono.java:4105)
        at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.drain(MonoIgnoreThen.java:172)
        at reactor.core.publisher.MonoIgnoreThen.subscribe(MonoIgnoreThen.java:56)
        at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:55)
        at reactor.netty.http.server.HttpServerHandle.onStateChange(HttpServerHandle.java:64)
        at reactor.netty.tcp.TcpServerBind$ChildObserver.onStateChange(TcpServerBind.java:228)
        at reactor.netty.http.server.HttpServerOperations.onInboundNext(HttpServerOperations.java:465)
        at reactor.netty.channel.ChannelOperationsHandler.channelRead(ChannelOperationsHandler.java:90)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:377)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:363)
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:355)
        at reactor.netty.http.server.HttpTrafficHandler.channelRead(HttpTrafficHandler.java:167)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:377)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:363)
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:355)
        at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:436)
        at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:321)
        at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:295)
        at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:251)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:377)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:363)
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:355)
        at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:377)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:363)
        at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
        at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163)
        at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:714)
        at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:650)
        at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:576)
        at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493)
        at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989)
        at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
        at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
        at java.base/java.lang.Thread.run(Thread.java:830)

当遵循堆栈跟踪时,我看到属性reDirectUriTemboard客户注册中是null。但是,由于我使用的是客户端凭据,因此根本不需要重定向URI。

我的配置有错误吗?如何使WebClient与客户端凭据流一起工作?


共有1个答案

伍弘盛
2023-03-14

应用程序现在正在使用此配置:

@Configuration
@RequiredArgsConstructor
@Slf4j
class Auth0Configuration {

    // some properties

    @Bean
    WebClient auth0WebClient(ReactiveOAuth2AuthorizedClientManager authorizedClientManager) {
        var exchangeFilterFunction = new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
        exchangeFilterFunction.setDefaultClientRegistrationId(AUTH_0_CLIENT_REGISTRATION_ID);
        return WebClient.builder()
                .baseUrl(auth0ApiBaseUrl)
                .filter(exchangeFilterFunction)
                .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                .build();
    }

    @Bean
    public ReactiveClientRegistrationRepository reactiveClientRegistrationRepository() {
        return new InMemoryReactiveClientRegistrationRepository(
                ClientRegistration.withRegistrationId(AUTH_0_CLIENT_REGISTRATION_ID)
                        .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
                        .tokenUri(auth0TokenUri)
                        .clientAuthenticationMethod(ClientAuthenticationMethod.POST)
                        .clientId(auth0ClientId)
                        .clientSecret(auth0ClientSecret)
                        .scope()
                        .build()
        );
    }

    @Bean
    public ReactiveOAuth2AuthorizedClientManager reactiveOAuth2AuthorizedClientManager(ReactiveClientRegistrationRepository clientRegistrationRepository) {
        DefaultReactiveOAuth2AuthorizedClientManager authorizedClientManager =
                new DefaultReactiveOAuth2AuthorizedClientManager(clientRegistrationRepository, new UnAuthenticatedServerOAuth2AuthorizedClientRepository());
        authorizedClientManager.setAuthorizedClientProvider(ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
                .clientCredentials(clientCredentialsGrantBuilder -> {
                            WebClientReactiveClientCredentialsTokenResponseClient accessTokenResponseClient = new WebClientReactiveClientCredentialsTokenResponseClient();
                            accessTokenResponseClient.setWebClient(WebClient.builder()
                                    .filter((request, next) -> {
                                        FormInserter<String> body = (FormInserter<String>) request.body();
                                        body.with("audience", auth0Audience);
                                        return next.exchange(request);
                                    })
                                    .build());
                            clientCredentialsGrantBuilder.accessTokenResponseClient(accessTokenResponseClient);
                        }
                )
                .build()
        );
        return authorizedClientManager;
    }

}
 类似资料:
  • 这是Kotlin的bug,还是和Android Studio有关?

  • 我正在尝试对“https://graph.windows.net/{{tenantId}}/accounts?api-version=1.6”进行API调用 有什么想法吗?

  • 我目前是谷歌分析API throu JavaScript的新成员。我正在实现一些图表和第一次加载,页面是重定向我到谷歌网站登录与分析帐户,然后返回到我的原始页面,刷新和图表显示。 有没有任何技巧/想法,我可以得到任何API可以解决这个问题?

  • 快速入门展示了使用 IdentityServer 保护 API 的最基础的场景。 在这个场景中,我们定义一个 API,同时定义一个 想要访问这个 API 的 客户端。客户端将从 IdentityServer 请求获得一个访问令牌,然后用这个令牌来获得 API 的访问权限。 定义 API 范围(Scopes)用来定义系统中你想要保护的资源,比如 API。 由于当前演练中我们使用的是内存配置 —— 添

  • 我刚开始使用某种REST协议,在从cURL请求中取回访问令牌时遇到了困难。

  • 当授权范围限于客户端控制下的受保护资源或事先与授权服务器商定的受保护资源时客户端凭据可以被用作为一种授权许可。典型的当客户端代表自己表演(客户端也是资源所有者)或者基于与授权服务器事先商定的授权请求对受保护资源的访问权限时,客户端凭据被用作为授权许可。