Spring Security和MockMvc-需要模拟身份验证或主体

时间:2019-03-14 04:35:53

标签: spring spring-security spring-security-oauth2 mockmvc

我正在使用Spring Security,并面临编写控制器的单元测试用例(使用MockMvc)的问题。

我的控制器中有一个方法如下:

@GetMapping
public ResponseEntity<User> getUser(@AuthenticationPrincipal User activeUser){
    String userEmail = activeUser.getEmail();
    return userService.getUser(userEmail);
}

我收到500错误。

我尝试过的控制器的另一种变化是,它正在Postman / Curl上运行:

@GetMapping
public ResponseEntity<User> getUser(OAuth2Authentication authentication){
    String userEmail = (String) authentication.getUserAuthentication().getPrincipal();
    return userService.getUser(userEmail);
}

我的服务如下:

public ResponseEntity<User> getUser(String email) {
    return userRepository.findByEmail(email)
            .map(record -> ResponseEntity.ok().body(record))
            .orElse(ResponseEntity.notFound().build());
}

在此控制器方法的单元测试用例中,我有:

@Test
@WithMockUser(username = "1", password = "pwd", roles = "USER")
public void controller_should_get_user() throws Exception {
    when(userService.getUser("1")).thenReturn(new ResponseEntity(userMock, HttpStatus.OK));
    this.mockMvc.perform(MockMvcRequestBuilders.get("/api/user/")).andExpect(status().isOk());
}

我遇到以下错误:

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.NullPointerException
at com.timecloud.user.controller.UserControllerTest.controller_should_get_user(UserControllerTest.java:60)

如何使用当前身份验证传递或嘲笑用户?谢谢。

2 个答案:

答案 0 :(得分:0)

NullPointerException即将到来,因为您的测试无法找到OAuth2Authentication对象的任何内容。您可以做两件事来做测试用例:

  1. 尝试通过某些设置方法模拟OAuth2Authentication

OR

  1. 如果使用的是Spring 4.0+,最好的解决方案是使用 @ WithMockUser

    注释测试方法

    @Test @WithMockUser(username = "user1", password = "pwd", roles = "USER") public void mytest1() throws Exception { //Your test scenario }

答案 1 :(得分:0)

@WithMockUser创建一个UsernameAuthenticationToken,而不是OAuth2Authentication

这里至少有三种解决方案:

  1. 在安全上下文中注入OAuth2Authentication模拟或实例
  2. 将方法更改为public ResponseEntity<User> getUser(Authentication authentication),然后在内部使用authentication.getName()
  3. 使用一些现有工具为您应用解决方案1.,例如本libs I wrote

解决方案1的示例用法

@Test
public void test() throws Exception {
    final var storedRequest = mock(OAuth2Request);

    final var principal = mock(Principal.class);
    when(principal.getName()).thenReturn("user");

    final var userAuthentication = mock(Authentication.class);
    when(userAuthentication.getAuthorities()).thenReturn(Set.of(new SimpleGrantedAuthority("ROLE_USER"));
    when(userAuthentication.getPrincipal()).thenReturn(principal);

    final var oauth2Authentication = new OAuth2Authentication(storedRequest, authentication);

    SecurityContextHolder.getContext().setAuthentication(oauth2Authentication);

    // use MockMvc to test a @Controller or unit-test any other secured @Component as usual
}

解决方案3的示例用法

@Test
@WithMockAuthentication(authType = OAuth2Authentication.class, name = "user", authorities = "ROLE_USER")
public void test() throws Exception {
    // use MockMvc to test a @Controller or unit-test any other secured @Component as usual
}