Spring boot 如何在单元测试期间通过SymphileASF模板的身份验证

Spring boot 如何在单元测试期间通过SymphileASF模板的身份验证,spring-boot,spring-security,spring-webflux,spring-test,Spring Boot,Spring Security,Spring Webflux,Spring Test,我使用的是Spring Boot 2.0.8.0版本。我有一个控制器,它具有以下方法构造 @PostMapping(value = "/profile/change-password", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) public Mono<String> changePasswordSubmit(Authentication authentication, @RequestBody Multi

我使用的是Spring Boot 2.0.8.0版本。我有一个控制器,它具有以下方法构造

@PostMapping(value = "/profile/change-password", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
    public Mono<String> changePasswordSubmit(Authentication authentication, @RequestBody MultiValueMap<String, String> formData) {
@PostMapping(value=“/profile/change password”,consumes=MediaType.APPLICATION\u FORM\u URLENCODED\u value)
公共Mono changePasswordSubmit(身份验证,@RequestBody多值映射formData){
我的单元测试如下所示:

@RunWith(SpringRunner.class)
@WebFluxTest(controllers = ChangePasswordController.class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Import({ThymeleafAutoConfiguration.class, SpringSecurityContextUtils.class})
@WithMockUser(username = "test", password = "password")
public class ChangePasswordControllerTest {

    @Autowired
    WebTestClient webTestClient;
    @MockBean
    SpringUserDetailsRepository userDetailsRepository;

    @Autowired
    ChangePasswordController controller;

    @MockBean
    Authentication authentication;

    @Test
    public void addNewEntrySubmit() {
        MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
        formData.put("password1", Collections.singletonList("password"));
        formData.put("password2", Collections.singletonList("password"));

        webTestClient.post().uri("/profile/change-password").contentType(MediaType.APPLICATION_FORM_URLENCODED)
                .body(BodyInserters.fromFormData(formData)).exchange().expectStatus().isSeeOther().expectHeader().valueEquals(HttpHeaders.LOCATION, "/page/1");

//        verify(userDetailsRepository).updatePassword(captor.capture(), captor.capture());
        doNothing().when(userDetailsRepository).updatePassword(any(), any());
    }
}
@RunWith(SpringRunner.class)
@WebFluxTest(控制器=ChangePasswordController.class)
@FixMethodOrder(MethodSorters.NAME\u升序)
@导入({ThymeleafAutoConfiguration.class,SpringSecurityContextUtils.class})
@WithMockUser(username=“test”,password=“password”)
公共类ChangePasswordControllerTest{
@自动连线
WebTestClient-WebTestClient;
@蚕豆
SpringUserDetailsRepository用户详细信息Repository;
@自动连线
ChangePasswordController;
@蚕豆
认证;
@试验
public void addNewEntrySubmit(){
MultiValueMap formData=新链接的MultiValueMap();
formData.put(“password1”,Collections.singletonList(“password”);
formData.put(“密码2”,Collections.singletonList(“密码”);
webTestClient.post().uri(“/profile/change password”).contentType(MediaType.APPLICATION\u FORM\u URLENCODED)
.body(BodyInserters.fromFormData(formData)).exchange().expectStatus().isSeeOther().expectHeader().valueEquals(HttpHeaders.LOCATION,“/page/1”);
//验证(userDetailsRepository).updatePassword(captor.capture(),captor.capture());
doNothing().when(userDetailsRepository).updatePassword(any(),any());
}
}
我的问题是,当我运行测试时,控制器上的身份验证值为空。我已尝试添加安全上下文,但在正确操作时遇到问题。如何解决此问题

更新:
链接到示例存储库:

在Spring Boot 5.1.x之前,您需要手动添加Spring安全筛选器配置:

WebTestClient webTestClient = WebTestClient
        .bindToController(new ChangedPasswordController())
        .webFilter(new SecurityContextServerWebExchangeWebFilter())
        .apply(springSecurity())
        .configureClient()
        .build();
在5.1.x中,
@WebFluxTest
会自动添加这些调用,因此您不必这样做


在@jzheaux的帮助和相关文档以及webflux指南之后,您可以在中看到一个示例

我的单元测试如下所示:

    @RunWith(SpringRunner.class)
    @Import({ThymeleafAutoConfiguration.class})
    @WebFluxTest(controllers = ChangePasswordController.class)
    @WithMockUser(username = "test", authorities = {"ROLE_ADMIN"})
    @ContextConfiguration 
    public class ChangePasswordControllerTest {

    @Autowired
    ApplicationContext context;

    private WebTestClient webTestClient;

    @MockBean
    SpringUserDetailsRepository userDetailsRepository;

    @Captor
    private ArgumentCaptor<String> captor;

    @Before
    public void setUp() throws Exception {
        webTestClient = WebTestClient.bindToApplicationContext(context)
                .webFilter(new SecurityContextServerWebExchangeWebFilter())
                .apply(springSecurity())
                .configureClient()
                .build();
    }

    @Test
    public void getChangePasswordPageTest() {
        EntityExchangeResult<String> result = webTestClient
                .mutateWith(csrf())
                .get().uri("/profile/change-password")
                .exchange()
                .expectStatus().isOk()
                .expectBody(String.class).returnResult();

        assertThat(result.getResponseBody(), stringContainsInOrder(Arrays.asList("<title>Change Password</title>",
                "<input type=\"password\" class=\"form-control\" id=\"password1\" name=\"password1\">")));
    }

    @Test
    public void addNewEntrySubmit() {
        MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
        formData.put("password1", Collections.singletonList("password"));
        formData.put("password2", Collections.singletonList("password"));

        given(userDetailsRepository.updatePassword(any(), any())).willReturn(Mono.empty());

        webTestClient.mutateWith(csrf()).post().uri("/profile/change-password").contentType(MediaType.APPLICATION_FORM_URLENCODED)
                .body(BodyInserters.fromFormData(formData)).exchange().expectStatus().isSeeOther().expectHeader().valueEquals(HttpHeaders.LOCATION, "/page/1");

        verify(userDetailsRepository).updatePassword(captor.capture(), captor.capture());
//        doNothing().when(userDetailsRepository).updatePassword(any(), any());
    }
}```
@RunWith(SpringRunner.class)
@导入({terymeleafautoconfiguration.class})
@WebFluxTest(控制器=ChangePasswordController.class)
@WithMockUser(username=“test”,authorities={“ROLE\u ADMIN”})
@上下文配置
公共类ChangePasswordControllerTest{
@自动连线
应用上下文上下文;
私有WebTestClient WebTestClient;
@蚕豆
SpringUserDetailsRepository用户详细信息Repository;
@俘虏
私家侦探;
@以前
public void setUp()引发异常{
webTestClient=webTestClient.bindToApplicationContext(上下文)
.webFilter(新的SecurityContextServerWebExchangeWebFilter())
.apply(springSecurity())
.configureClient()
.build();
}
@试验
public void getChangePasswordPageTest(){
EntityExchangeResult结果=webTestClient
.mutateWith(csrf())
.get().uri(“/profile/change password”)
.exchange()
.expectStatus().isOk()
.expectBody(String.class).returnResult();
assertThat(result.getResponseBody(),stringContainsInOrder(Arrays.asList(“更改密码”),
"")));
}
@试验
public void addNewEntrySubmit(){
MultiValueMap formData=新链接的MultiValueMap();
formData.put(“password1”,Collections.singletonList(“password”);
formData.put(“密码2”,Collections.singletonList(“密码”);
给定(userDetailsRepository.updatePassword(any(),any()).willReturn(Mono.empty());
webTestClient.mutateWith(csrf()).post().uri(“/profile/change password”).contentType(MediaType.APPLICATION\u FORM\u URLENCODED)
.body(BodyInserters.fromFormData(formData)).exchange().expectStatus().isSeeOther().expectHeader().valueEquals(HttpHeaders.LOCATION,“/page/1”);
验证(userDetailsRepository).updatePassword(captor.capture(),captor.capture());
//doNothing().when(userDetailsRepository).updatePassword(any(),any());
}
}```

似乎还有其他事情发生。当我将您的测试和控制器复制到我的应用程序中时,测试完成得很好。我不得不对其进行调整以删除一些内部构件并添加csrf令牌,但您可以在.Hi@jzheaux上看到我的示例应用程序,谢谢,如果我将您的Spring Boot版本更改为2.0.8.RELEASE sa我和我的一样,它失败了,错误也一样。我已经用spring启动版本更新了问题。你好@jzheaux,谢谢你。但是我已经尝试了这种方法和文档中的方法。我仍然有很多问题,所以现在两个测试都失败了,提交错误与以前相同,getthepage找不到视图。我已经完成了将问题与复制问题的project链接一起注明日期,谢谢我已经下载了您的代码,幸运的是,
身份验证
已正确解决。如果您调试到控制器方法中,您将看到
身份验证
实例确实存在。测试抛出NPE的原因是因为没有behavior嘲笑了userDetailsRepository。嗨,经过大量的尝试和错误,我让一切都正常工作了。不得不稍微将其更改为bindToApplicationContext,这样它就能找到其他页面。下面是最终解决方案。太棒了!很高兴听到这个消息。