简体   繁体   中英

Spring Boot 2: test secured endpoints

I have a project build up with spring boot 2 (data, rest, security...), so in the project I have rest controllers like this:

@RestController
class ProductController {

private final ProductService productService;
private final ProductConverter productConverter;
private final UserService userService;

@Autowired
ProductController(ProductService productService,
                  ProductConverter productConverter,
                  UserService userService) {
    this.productService = productService;
    this.productConverter = productConverter;
    this.userService = userService;
}

@GetMapping(EndpointPath.PRODUCTS)
PageableProductsDTO getProducts(Principal principal, Pageable pageable) {
    return productService.getUsersProducts(principal.getName(), pageable);
}

@PostMapping(EndpointPath.PRODUCT)
ResponseEntity<?> createNewProduct(@Valid @RequestBody ProductDetailsDto productDetailsDto, Principal principal) {
   productService.saveProduct(productConverter.createFrom(productDetailsDto), principal.getName());
   return ResponseEntity.status(HttpStatus.CREATED).build();
}
}

and also security configuration:

@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

private final RESTLogoutSuccessHandler restLogoutSuccessHandler;

@Autowired
public ResourceServerConfig(RESTLogoutSuccessHandler restLogoutSuccessHandler) {
    this.restLogoutSuccessHandler = restLogoutSuccessHandler;
}

@Override
public void configure(HttpSecurity http) throws Exception {
    http.csrf().disable()
            .anonymous()
                .disable()
            .logout()
                .logoutUrl(EndpointPath.SIGN_OUT)
                .logoutSuccessHandler(restLogoutSuccessHandler)
            .and()
            .authorizeRequests()
                .antMatchers(GET, EndpointPath.PRODUCTS).authenticated()
                .antMatchers(POST, EndpointPath.PRODUCT).authenticated()
            .and()
                .exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
}
}

Also I coverde ProductController with unit tests like this:

@ExtendWith({SpringExtension.class, MockitoExtension.class})
@ContextConfiguration(classes = {MessageSourceConfiguration.class})
@MockitoSettings(strictness = Strictness.LENIENT)
class ProductControllerTest {

private MockMvc mockMvc;

@Autowired
private MessageSource messageSource;

@Mock
private ProductService productService;

@Mock
private ProductConverter productConverter;

@Mock
private UserService userService;

@BeforeEach
void setUp() {
    mockMvc = MockMvcBuilders.standaloneSetup(new ProductController(productService, productConverter, userService))
                                              .setCustomArgumentResolvers(new PageableHandlerMethodArgumentResolver())
                                              .setControllerAdvice(new ControllerAdvice(messageSource)).build();
}

@Test
void shouldReturnSetOfProducts() throws Exception {
    // Given
    String authenticatedUserEmail = "email@g.com";
    when(productService.getUsersProducts(eq(authenticatedUserEmail), ArgumentMatchers.any(Pageable.class))).thenReturn(new PageableProductsDTO(new PageImpl<>(
                                                                        List.of(new ProductDetailsDto("1234", "name", 1), new ProductDetailsDto("12345", "name 2", 2)))));

    // When - Then
    mockMvc.perform(get(EndpointPath.PRODUCTS).principal(() -> authenticatedUserEmail).accept(MediaType.APPLICATION_JSON))
            .andExpect(status().isOk())
            .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
            .andExpect(jsonPath("$.productDtos.content", hasSize(2)))
            .andExpect(jsonPath("$.productDtos.totalPages", is(1)))
            .andExpect(jsonPath("$.productDtos.totalElements", is(2)))
            .andExpect(jsonPath("$.productDtos.content.[*].barcode", containsInAnyOrder("1234", "12345")))
            .andExpect(jsonPath("$.productDtos.content.[*].name", containsInAnyOrder("name", "name 2")))
            .andExpect(jsonPath("$.productDtos.content.[*].price", containsInAnyOrder(1, 2)));
}
}

beside that unit tests I want to make sure that the endpoint EndpointPath.PRODUCTS is secured. Does anybody know how to write a unit test for this? For example: expected status 401/403 when user is not authenticated/authorized.

The simplest test, full on Spring Boot integration test, would look like this

@ExtendWith(SpringExtension.class)
@SpringBootTest
@AutoConfigureMockMvc
@DisplayName("sample boot integration tests")
public class SpringBootIntegrationTests {

    @Autowired
    private MockMvc mvc;

    @DisplayName("pages are secured")
    void pageIsSecure() throws Exception {
        mvc.perform(
            MockMvcRequestBuilders.get("/products")
        )
            .andExpect(status().is3xxRedirection())
            .andExpect(redirectedUrl("http://localhost/login"))
            .andExpect(unauthenticated())
        ;
    }
}

Typically you will not see a 401/403 if there is no authentication. Cause Spring Security will redirect you to the authorization server.

The redirectUrl would be something that matches the redirect to the authorization server.

So it could be something like

    redirectedUrl(containsString("http://authorization.server/oauth/authorize"))

If you have an endpoint that expects a token, you could do something like this in your test

    .andExpect(status().isUnauthorized())
    .andExpect(unauthenticated())

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM