簡體   English   中英

Spring-Boot WebMvcTest:如何使用身份驗證 object 參數測試 controller 方法?

[英]Spring-Boot WebMvcTest: How to test controller method with Authentication object parameter?

這是這個問題Spring WebMvcTest 如何模擬身份驗證的延續?

我正在嘗試在 Spring-boot 中測試 controller 方法,該方法接收Authentication object 作為參數。 controller 是一個帶有RestController注釋的@CrossOrigin 該方法如下所示:

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

如您所見,我從參數中獲取了身份驗證的主體。

問題是,在我的WebMvcTest測試用例中,我得到了NullPointerException ,因為在測試用例中, authentication似乎是 null。 我的問題是為什么?

我嘗試添加一個given的調用,該調用將在測試用例的@PostConstruct注釋方法中返回自定義UserDetails object ,但我仍然得到NullPointerException

我的測試用例如下所示:

@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方法中提供它?

可以在此處找到具有重現錯誤的最少代碼的 GitHub 項目。 https://github.com/Kars1090/SpringSecurityTest

謝謝!

克隆您的項目后,我已經在您的 controller 方法中獲得了有效的Authentication object。 基本上,您的測試中有兩個主要問題:

  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

您的 controller 方法:

@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());
   }
 }

一個(不好的)解決方法是從 controller 參數中刪除authentication ,而是通過SecurityContextHolder.getContext().getAuthentication()獲取身份驗證。

這將使測試工作而無需更改任何其他內容。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM