Spring-Boot WebMvcTest:如何使用身份验证对象参数测试控制器方法? [英] Spring-Boot WebMvcTest: How to test controller method with Authentication object parameter?

查看:30
本文介绍了Spring-Boot WebMvcTest:如何使用身份验证对象参数测试控制器方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是这个问题的延续Spring WebMvcTest 如何模拟身份验证?

我正在尝试在 Spring-boot 中测试一个控制器方法,该方法接收一个 Authentication 对象作为参数.控制器是一个带有 @CrossOrigin 注释的 RestController.该方法如下所示:

I'm trying to test a controller method in Spring-boot that receives an Authentication object as parameter. The controller is a RestController with @CrossOrigin annotation. The method looks like this:

@GetMapping("/authentication")
public String testAuthentication(Authentication authentication) {
    UserDetailsStub userDetailsStub = (UserDetailsStub) authentication.getPrincipal();
    return userDetailsStub.getUsername();
}

如您所见,我从 Authentication 参数中获取了主体.

As you can see i get the principal from the Authentication out of the parameters.

问题是,在我的 WebMvcTest 测试用例中,我得到一个 NullPointerException,因为在测试用例中,authentication 似乎为空.我的问题是为什么?

The problem is, in my WebMvcTest test case i get a NullPointerException because in the test case, authentication seems to be null. My question is why?

我尝试添加一个 given 调用,该调用将在测试用例中的 @PostConstruct 注释方法中返回一个自定义的 UserDetails 对象,但是我仍然得到 NullPointerException.

I have tried adding a given call which will return a custom UserDetails object in a @PostConstruct annotated metho in the test case, but still i get the NullPointerException.

我的测试用例如下所示:

My test case looks like this:

@Import(SecurityConfiguration.class)
@RunWith(SpringRunner.class)
@WebMvcTest(PDPController.class)
@AutoConfigureMockMvc(addFilters = false)
public class PDPControllerTests {

    @Autowired
    private MockMvc mvc;

    @MockBean(name = "userDetailsService")
    private MyUserDetailsService userDetailsService;
    
    //..

    @PostConstruct
    public void setup() {
        given(userDetailsService.loadUserByUsername(anyString()))
                .willReturn(new UserDetailsStub());
    }
    
    //..

    @Test
    @WithUserDetails(value = "username", userDetailsServiceBeanName = "userDetailsService")
    public void testAuthentication() throws Exception {
        mvc.perform(get("/pdps/authentication").secure(true)
                .contentType(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk());
    }
    
}

为什么在测试用例中 authentication 为 null,即使我在 @PostConstruct 方法中提供它?

Why is authentication null in the test case, even when i supply it in the @PostConstruct method?

可以在此处找到重现错误的代码最少的 GitHub 项目.https://github.com/Kars1090/SpringSecurityTest

A GitHub project with minimal code that reproduces the error can be found here. https://github.com/Kars1090/SpringSecurityTest

谢谢!

推荐答案

克隆您的项目后,我已实现在您的控制器方法中接收有效的 Authentication 对象.基本上,您在测试中有两个主要问题:

After clone your project I have achieved to receive a valid Authentication object in your controller method. Basically you have 2 main problems in your test:

  1. 不必要的额外配置
  2. 过滤器的错误模拟配置:JwtRequestFilter

总的来说,变化如下:

public class UserDetailsStub implements UserDetails {

  private String username;
  private String password;
  private Collection<? extends GrantedAuthority> authorities;

  public UserDetailsStub() {}
        
  public static UserDetailsStub of (User user) {
    UserDetailsStub userDetails = new UserDetailsStub();
    if (null != user) {
        userDetails.username = user.getUsername();
        userDetails.password = user.getPassword();
        userDetails.authorities = user.getAuthorities();
    }
    return userDetails;
  }

  @Override
  public Collection<? extends GrantedAuthority> getAuthorities() {
    return authorities;
  }

  @Override
  public String getPassword() {
    return password;
  }

  @Override
  public String getUsername() {
    return username;
  }
  // Rest of the code is equal to your version

您的控制器方法:

@GetMapping("/authentication")
public String testAuthentication(Authentication authentication) {
  UserDetailsStub userDetailsStub = UserDetailsStub.of((User) 
    authentication.getPrincipal());
  return userDetailsStub.getUsername();
}

和测试:

@WebMvcTest(value = PDPController.class)
public class PDPControllerTests {

  @Autowired
  private MockMvc mvc;

  /** You have not to mock the filter because in that case Spring
   * won't know how to deal with it, when the list of them
   * should be managed.
   *
   * That is the reason why you had to include
   * @AutoConfigureMockMvc(addFilters = false), but that
   * is preciselly what was avoiding the creation of your
   * Authentication object, because your JwtRequestFilter
   * was not being executed.
   *
   * With the current code, your filter will be executed and
   * the Authentication object created.
   */
   //@MockBean
   //private JwtRequestFilter jwtRequestFilter;

   // What you have to mock are the classes the filter uses internally
   @MockBean
   private MyUserDetailsService userDetailsService;

   @MockBean
   private JwtService jwtService;

   @Test
   @WithMockUser
   public void test() throws Exception {
     mvc.perform(
            get("/pdps/authentication").secure(true)
                    .contentType(MediaType.APPLICATION_JSON))
            .andExpect(status().isOk());
   }
 }

这篇关于Spring-Boot WebMvcTest:如何使用身份验证对象参数测试控制器方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆