Reputation: 12896
Assume I have the following @WebMvcTest
and @RestController
in a Spring boot applcation (version 2.4.2).
// the test
@Test
@WithUserDetails
public void should_return_ok() throws Exception {
mockMvc.perform(get("/api/products").andExpect(status().isOk());
}
// the controller
@GetMapping(path = "/api/products")
public ResponseEntity<List<Product>> getProducts(@AuthenticationPrincipal CustomUserDetails userDetails) {
List<Product> products = productService.getProductsByUserId(userDetails.getUserId());
return ResponseEntity.ok(products);
}
I also provided a CustomUserDetails
class which adds a userId
.
@Getter
@Setter
public class CustomUserDetails extends User {
private static final long serialVersionUID = 5540615754152379571L;
private Long userId;
public CustomUserDetails(String username, String password, Collection<? extends GrantedAuthority> authorities) {
super(username, password, authorities);
}
public CustomUserDetails(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) {
super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
}
}
I understand that Spring provides the @WithUserDetails
annotation to provide an adequate object for testing. And this also allows specifying a custom username, password, etc. However I don't know how I could provide the userId
which is necessary so that the controller method can extract it from the CustomUserDetails
object.
Upvotes: 1
Views: 1025
Reputation: 6288
In your implementation of UserDetailsService
you should return your instance of UserDetails
. For example:
@Override
public UserDetails loadByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("Username " + username + " not found");
}
CustomUserDetails customUserDetails = new CustomUserDetails(user);
customUserDetails.setUserId(user.getUserId());
return customUserDetails;
}
public class CustomUserDetails implements UserDetails {
private final Long userId;
private final User user;
...constructors
...getters and setters
}
In your code, you can cast the Authentication
object to your CustomUserDetails
.
CustomUserDetails customUserDetails = (CustomUserDetails) SecurityContextHolder.getContext().getAuthentication();
Long userId = customUserDetails.getUserId();
Upvotes: 0
Reputation: 1082
You can create your own custom UserDetails object in your test class and do the following:
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
CustomUserDetails customUserDetails = new CustomUserDetails(...);
mockMvc.perform(get("/api/products").with(user(customUserDetails))).andExpect(status().isOk());
Upvotes: 1