Spring WebFlux 请求主体在测试中为空

Posted

技术标签:

【中文标题】Spring WebFlux 请求主体在测试中为空【英文标题】:Spring WebFlux Request Principal is null in test 【发布时间】:2021-03-03 03:15:37 【问题描述】:

我们有一个库,它为我们的请求提供了一个自定义过滤器,我们用它来将有关请求的信息写入我们的日志。我们以前只需要 servlet 支持,但现在需要添加 webflux 支持。我创建了一个新的 WebFilter,它按预期工作。

但是,尝试测试这个新过滤器很困难。我们在日志的用户信息请求中依赖于主体,但在测试期间,它始终为空。我尝试了很多东西,包括以下示例:https://docs.spring.io/spring-security/site/docs/5.1.0.RELEASE/reference/html/test-webflux.html

我的过滤器:

package com.project.utils.security.request.logging.config.autoconfig;

import com.project.utils.logging.LoggingUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;

public class ReactiveRequestLoggingFilter implements WebFilter 
  private static final Logger logger = LoggerFactory.getLogger(ReactiveRequestLoggingFilter.class);

  @Override
  public Mono<Void> filter(ServerWebExchange serverWebExchange, WebFilterChain webFilterChain) 
    return serverWebExchange
        .getPrincipal()
        .flatMap(
            principal -> 
              LoggingUtil loggingUtil =
                  new LoggingUtil(serverWebExchange.getRequest(), principal.getName());
              loggingUtil.setEvent("Http Request");
              logger.info(loggingUtil.toString());
              return webFilterChain.filter(serverWebExchange);
            );
  

我的测试:

package com.project.utils.security.request.logging.config.autoconfig;

import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.mockUser;
import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.springSecurity;
import com.project.utils.security.request.logging.config.testcontrollers.ReactiveTestController;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.reactive.server.WebTestClient;

@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = ReactiveTestController.class)
class ReactiveRequestLoggingFilterTest 
  @Autowired ReactiveTestController controller;
  ReactiveRequestLoggingFilter filter = new ReactiveRequestLoggingFilter();

  @Test
  void test() 
    WebTestClient client =
        WebTestClient.bindToController(controller)
            .webFilter(filter)
            .apply(springSecurity())
            .configureClient()
            .build()
            .mutateWith(mockUser("User"));
    client
        .get()
        .uri("/test")
        .exchange()
        .expectStatus()
        .isOk()
        .expectBody(String.class)
        .isEqualTo("GET");
  

我用于测试的控制器:

package com.project.utils.security.request.logging.config.testcontrollers;

import com.project.utils.security.request.logging.config.annotations.EnableReactiveRequestLogging;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;

@RestController
@RequestMapping("/test")
@EnableReactiveRequestLogging
public class ReactiveTestController 
  @GetMapping
  Mono<ResponseEntity<String>> get() 
    return Mono.just(ResponseEntity.ok("GET"));
  

我们使用的是 Spring Boot 2.1,它使用 Spring Security 5.1。就像我说的,实际的功能是有效的,但是我们需要为它编写测试,我不知道如何让委托人进入请求。

谢谢!

【问题讨论】:

而您需要通过身份验证才能访问/test?否则,Spring Security 不会应用于该特定端点,那么在向该端点发出请求时就没有主体的概念。 我需要对其进行身份验证,因为过滤器依赖于存在的主体。 【参考方案1】:

好的,从这个答案中找到了一个可行的解决方案:https://***.com/a/55426458/1370823

我的最终测试如下:

package com.project.utils.security.request.logging.config.autoconfig;

import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.springSecurity;
import com.project.utils.security.request.logging.config.testcontrollers.ReactiveTestController;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.security.web.server.context.SecurityContextServerWebExchangeWebFilter;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.reactive.server.WebTestClient;

@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = ReactiveTestController.class, ReactiveRequestLoggingFilter.class)
@WebFluxTest(controllers = ReactiveTestController.class)
@WithMockUser(value = "Test User", username = "TestUser")
class ReactiveRequestLoggingFilterTest 
  @Autowired ReactiveTestController controller;
  @Autowired ReactiveRequestLoggingFilter filter;

  @Test
  void test() 
    WebTestClient client =
        WebTestClient.bindToController(controller)
            .webFilter(new SecurityContextServerWebExchangeWebFilter())
            .webFilter(filter)
            .apply(springSecurity())
            .build();
    client
        .get()
        .uri("/test")
        .exchange()
        .expectStatus()
        .isOk()
        .expectBody(String.class)
        .isEqualTo("GET");
  


【讨论】:

以上是关于Spring WebFlux 请求主体在测试中为空的主要内容,如果未能解决你的问题,请参考以下文章

Spring Security WebFlux - 带有身份验证的主体

如何使用 spring webflux 读取请求正文

`NativeWebRequest` 在使用 Spring Boot 的传入请求中为空

Webflux 请求处理程序中 ServerRequest 返回的 null 主体

如何在 Spring Boot 2(w/WebFlux)中为 HTTP 和 HTTPS 配置两个端口?

在 Spring Webflux 中为给定路径禁用身份验证和 csrf?