WebClient 调用抛出 401 状态,但收到 500 状态响应

问题描述 投票:0回答:1

我正在尝试实现一个 AuthorizationHandlerAdapter (实现

org.springframework.web.reactive.HandlerAdapter
接口)。它调用外部端点来获取用户的权限。

    @Component
    @Slf4j
    public class AuthorisationHandlerAdapter implements HandlerAdapter {
    
      @Autowired
      RequestMappingHandlerAdapter handlerAdapter;
      @Autowired
      AuthWebClient authWebClient;
    
      @Override
      public boolean supports(Object handler) {
        return true;
      }
    
      @Override
      public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {
        return authorizeInternal(exchange, ImmutableList.of("user.view"))
            .then(handlerAdapter.handle(exchange, handler));
      }
    
      protected Mono<Void> authorizeInternal(ServerWebExchange exchange, List<String> requiredPermission) {
    
        HttpHeaders headers = exchange.getRequest().getHeaders();
        String sub = headers.get("sub").get(0);
        String auth = Objects.requireNonNull(headers.get("Authorization")).get(0); //remove todo
        return authWebClient.getAuthorities(sub, auth)
            .flatMap(authorities -> {
              List<String> serviceAuthorities = authorities.getPermissions();
              if (!serviceAuthorities.containsAll(requiredPermission)) {
                return Mono.error(new ResponseStatusException(HttpStatus.FORBIDDEN, "Forbidden access"));
              }
              return Mono.when();
            })
            ;
      }

此 Web 客户端调用外部端点:

    @Service
    @Slf4j
    public class AuthWebClient {
    
      @Autowired
      private WebClient webClient;
    
      public Mono<Authorities> getAuthorities(String subject, String auth) {
        URI uri = UriComponentsBuilder.fromUriString(-someUrl-).toUri();

        Authorities authoritiesRequest = Authorities.builder().includePermissions(true).build();
        HttpHeaders headers = new HttpHeaders();
        headers.set("Authorization", auth);
        return webClient.post()
            .uri(uri)
            .headers(httpHeaders -> httpHeaders.addAll(headers))
            .body(BodyInserters.fromValue(authoritiesRequest))
            .accept(MediaType.APPLICATION_JSON)
            .retrieve()
            .bodyToMono(AuthoritiesResponse.class);
      }
    
    
    }

但是,当外部端点发送 401 未授权状态时,系统会将 401 错误包装为 500。我需要将 401 状态传播给用户。可能我遗漏了一些点。

以下堆栈跟踪打印在控制台上:

    2022-05-19 19:03:20.229 ERROR 58468 --- [ctor-http-nio-4] a.w.r.e.AbstractErrorWebExceptionHandler : [addd567f-2]  500 Server Error for HTTP GET "/org-services/org"
    
    org.springframework.web.reactive.function.client.WebClientResponseException$Unauthorized: 401 Unauthorized from POST https://someUrl/subjects/6d0b-asdf-asd-f2323/permissions
        at org.springframework.web.reactive.function.client.WebClientResponseException.create(WebClientResponseException.java:198)
        Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
    Error has been observed at the following site(s):
        *__checkpoint ⇢ 401 from POST https://someUrl/subjects/6d0b-asdf-asd-f2323/permissions [DefaultWebClient]
        *__checkpoint ⇢ -packageName-.api.filter.TokenResolverFilter [DefaultWebFilterChain]
        *__checkpoint ⇢ -packageName-.api.filter.UnsuccessfulResponseFilter$$Lambda$1421/415917546 [DefaultWebFilterChain]
        *__checkpoint ⇢ -packageName-.api.config.CorsConfiguration$$Lambda$1420/297366988 [DefaultWebFilterChain]
        *__checkpoint ⇢ -packageName-.api.filter.GzipDecompressionFilter [DefaultWebFilterChain]
        *__checkpoint ⇢ org.springframework.boot.actuate.metrics.web.reactive.server.MetricsWebFilter [DefaultWebFilterChain]
        *__checkpoint ⇢ HTTP GET "/***/org/keys


----------


" [ExceptionHandlingWebHandler]
    Stack trace:
            at org.springframework.web.reactive.function.client.WebClientResponseException.create(WebClientResponseException.java:198)
            at org.springframework.web.reactive.function.client.DefaultClientResponse.lambda$createException$1(DefaultClientResponse.java:207)
            at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:106)
            at -packageName-.api.lifter.MdcContextLifter.onNext(MdcContextLifter.java:37)
            at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:79)
            at -packageName-.api.lifter.MdcContextLifter.onNext(MdcContextLifter.java:37)
            at reactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.onNext(FluxDefaultIfEmpty.java:101)
            at -packageName-.api.lifter.MdcContextLifter.onNext(MdcContextLifter.java:37)
            at reactor.core.publisher.FluxHide$SuppressFuseableSubscriber.onNext(FluxHide.java:137)
            at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:127)
            at reactor.core.publisher.FluxContextWrite$ContextWriteSubscriber.onNext(FluxContextWrite.java:107)
            at -packageName-.api.lifter.MdcContextLifter.onNext(MdcContextLifter.java:37)
            at reactor.core.publisher.FluxHide$SuppressFuseableSubscriber.onNext(FluxHide.java:137)
            at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:127)
            at -packageName-.api.lifter.MdcContextLifter.onNext(MdcContextLifter.java:37)
            at reactor.core.publisher.FluxHide$SuppressFuseableSubscriber.onNext(FluxHide.java:137)
            at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.onNext(FluxFilterFuseable.java:118)
            at -packageName-.api.lifter.MdcContextLifter.onNext(MdcContextLifter.java:37)
            at reactor.core.publisher.FluxHide$SuppressFuseableSubscriber.onNext(FluxHide.java:137)
            at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1816)
            at reactor.core.publisher.MonoCollect$CollectSubscriber.onComplete(MonoCollect.java:159)
            at -packageName-.api.lifter.MdcContextLifter.onComplete(MdcContextLifter.java:48)
            at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:142)
            at -packageName-.api.lifter.MdcContextLifter.onComplete(MdcContextLifter.java:48)
            at reactor.core.publisher.FluxPeek$PeekSubscriber.onComplete(FluxPeek.java:260)
            at -packageName-.api.lifter.MdcContextLifter.onComplete(MdcContextLifter.java:48)
            at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:142)
            at reactor.netty.channel.FluxReceive.onInboundComplete(FluxReceive.java:400)
            at reactor.netty.channel.ChannelOperations.onInboundComplete(ChannelOperations.java:419)
            at reactor.netty.channel.ChannelOperations.terminate(ChannelOperations.java:473)
            at reactor.netty.http.client.HttpClientOperations.onInboundNext(HttpClientOperations.java:702)
            at reactor.netty.channel.ChannelOperationsHandler.channelRead(ChannelOperationsHandler.java:93)
            at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
            at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
            at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
            at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:103)
            at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
            at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
            at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
            at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:436)
            at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:324)
            at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:296)
            at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:251)
            at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
            at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
            at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
            at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1372)
            at io.netty.handler.ssl.SslHandler.decodeJdkCompatible(SslHandler.java:1235)
            at io.netty.handler.ssl.SslHandler.decode(SslHandler.java:1284)
            at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:507)
            at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:446)
            at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:276)
            at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
            at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
            at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
            at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
            at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
            at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
            at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
            at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166)
            at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:719)
            at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:655)
            at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:581)
            at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493)
            at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:986)
            at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
            at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
            at java.lang.Thread.run(Thread.java:750)
spring-boot spring-webflux
1个回答
0
投票
  1. WebClient 将带有错误状态代码(例如:4xx、5xx)的响应视为异常。
  2. 由于抛出异常(在本例中),spring的异常处理程序将返回状态码500。

如果您想处理特定的 WebClient 响应状态代码,请使用 ExchangeFilterFunction 根据您的异常类型对其进行自定义。 (参见这个

然后为该异常定义异常处理程序(scope spring)。 (参见这个

© www.soinside.com 2019 - 2024. All rights reserved.