在 HandlerFilterFunction 中读取请求正文,如果有效则传递给下一个处理程序

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

用例是使用请求正文验证 JWT 中的数据。 JWT

sub
包含

{
  "sub": "{\"data\": {\"street\": \"Street 1\", \"landmark\": \"TallestTower\"}}"
}

请求必须包含准确的数据。不符则禁止,否则请继续处理。

使用:

  • Springboot 3,
  • WebFlux 与路由器功能反应
  • Jose4j 智威汤逊。

在调试期间,当它到达

ResourceRoutesHandler.create()
函数并尝试执行
request.bodyToMono().flatMap()
时,它只是以HTTP 200退出。
.flatMap()
中还有几个进一步的进程,但它们没有执行。有趣的是,工作区中没有
ServerResponse.ok()
可以使用 HTTP 200 产生这样的结果。

我的理解是这

ServerRequest.from(request).build()
导致了它。

有没有办法验证请求正文并发送相同的请求或在同一订阅内发送? 或者是否有其他方法可以在到达路由处理程序之前验证请求数据?

路线


@Configuration
@RequiredArgsConstructor
public class ResourceRoutes {

  final JwtHandlerFilterFunction jwtHandlerFilterFunction;

  public RouterFunction<ServerResponse> createResource(ResourceRoutesHandler recourceRoutesHandler) {
    return RouterFunction.route(
            POST("/create").and(accept(MediaType.APPLICATION_JSON)),
            recourceRoutesHandler::create
        )
        .filter(jwtHandlerFilterFunction);
  }
}

HandlerFilterFunction


@Component()
@RequiredArgsConstructor
public class JwtHandlerFilterFunction
    implements HandlerFilterFunction<ServerResponse, ServerResponse> {

  final ObjectMapper objectMapper;

  @Override
  public Mono<ServerResponse> filter(final ServerRequest request,
                                     final HandlerFunction<ServerResponse> next) {

    Headers headers = request.headers();
  
    Optional<String> jwtToken = Optional.ofNullable(headers.firstHeader("Auth-Token"));

    return jwtToken.map(_jwt -> jwtValidateService
                       .validateReactive(_jwt)
                       .flatMap(verifyPayload(request))
                       .flatMap(validMessage -> next.handle(
                        // clone here and pass on to next
                        // this is causing fail to execute request in route handler
                           ServerRequest.from(request)
                                        .build()
                       ))
                       .onErrorResume(throwable -> ServerResponse.status(HttpStatus.FORBIDDEN)
                           .contentType(MediaType.APPLICATION_JSON)
                           .bodyValue("Authorization failed")
                       ) // jwt validation failed
                   )
                   .orElse(next.handle(request)); // to next if header not present
  }

  private Function<String, Mono<String>> verifyPayload(final ServerRequest clientRequest) {
    return subject -> clientRequest
        .bodyToMono(String.class) // as the body already read how to pass on to next handler?
        .flatMap(requestData -> Mono.fromCallable(() -> {
          // for simplicity
          if (!subject.equals(requestData)) {
            throw new RuntimeException("Invalid subject data");
          }
          return "Valid";
        }));
  }

}

感谢您的宝贵时间

spring-boot spring-webflux project-reactor
1个回答
0
投票

我认为在

HandlerFilterFunction
中依赖请求正文不是一个好主意。请注意,
ServerRequest.from(...)
方法不会复制正文,原因类似:正文发布者通常只能订阅和使用一次。

不再进一步争论整个安排,我认为您在您的情况下可能有出路:因为您将整个正文视为

String
中的
verifyPayload
,您也许可以将两个 jwtToken
flatMap
合并为一个通过修改
verifyPayload
方法,如下所示:

private Function<String, Mono<String>> verifyPayload(final ServerRequest clientRequest, 
        //added the HandlerFunction as a parameter
        final HandlerFunction<ServerResponse> next) {
    return subject -> clientRequest
        .bodyToMono(String.class)
        .flatMap(body -> {
          // for simplicity
          if (!subject.equals(body)) {
            throw new RuntimeException("Invalid subject data");
          }
          //we have already read the full body, so we can "forward" it
          return next.handle(ServerRequest.from(clientRequest).body(body).build());
        }));
  }
© www.soinside.com 2019 - 2024. All rights reserved.