Spring Security para testes de integração do Spring Boot
1. Introdução
A capacidade de executar testes de integração sem a necessidade de um ambiente de integração independente é um recurso valioso para qualquer pilha de software. A integração perfeita do Spring Boot com o Spring Security simplifica o teste de componentes que interagem com uma camada de segurança.
Neste tutorial rápido, exploraremos o uso de@MockMvcTeste@SpringBootTest para executar testes de integração habilitados para segurança.
2. Dependências
Vamos primeiro trazer as dependências de que precisaremos para nosso exemplo:
org.springframework.boot
spring-boot-starter-security
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-test
test
org.springframework.security
spring-security-test
test
Os startersspring-boot-starter-web, spring-boot-starter-security,espring-boot-starter-test nos fornecem acesso aos utilitários de teste Spring MVC, Spring Security e Spring Boot.
Além disso, vamos trazerspring-security-test para obter acesso à anotação@WithMockUser que usaremos.
3. Configuração de Segurança da Web
Nossa configuração de segurança na web será direta. Apenas usuários autenticados poderão acessar caminhos que correspondam a/private/. Os caminhos que correspondem a/public/ estarão disponíveis para qualquer usuário:
@Configuration
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
auth.inMemoryAuthentication()
.passwordEncoder(encoder)
.withUser("spring")
.password(encoder.encode("secret"))
.roles("USER");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/private/**")
.authenticated()
.antMatchers("/public/**")
.permitAll()
.and()
.httpBasic();
}
}
4. Configuração de Segurança do Método
Além da segurança baseada em caminho de URL que definimos em nossoWebSecurityConfigurer,, podemos configurar a segurança baseada em método fornecendo um arquivo de configuração adicional:
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfigurer
extends GlobalMethodSecurityConfiguration {
}
Esta configuração permite suporte para anotações pré / pós do Spring Security. Outros atributos também estão disponíveis se for necessário suporte adicional. Para obter mais informações sobre Spring Method Security, dê uma olhada em nossoarticle on the topic.
5. Testando controladores com@WebMvcTest
Ao usar a abordagem de anotação@WebMvcTest com Spring Security,MockMvc is automatically configured with the necessary filter chain required para testar nossa configuração de segurança.
ComoMockMvc está configurado para nós, podemos usar@WithMockUser para nossos testes sem nenhuma configuração adicional:
@RunWith(SpringRunner.class)
@WebMvcTest(SecuredController.class)
public class SecuredControllerWebMvcIntegrationTest {
@Autowired
private MockMvc mvc;
// ... other methods
@WithMockUser(value = "spring")
@Test
public void givenAuthRequestOnPrivateService_shouldSucceedWith200() throws Exception {
mvc.perform(get("/private/hello").contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
}
Observe que usar@WebMvcTest dirá ao Spring Boot para instanciar apenas a camada da web e não todo o contexto. Por causa disso,controller tests that use @WebMvcTest will run faster than with other approaches.
6. Testando controladores com@SpringBootTest
Ao usar a anotação@SpringBootTest para testar controladores com Spring Security,it’s necessary to explicitly configure the filter chain when setting up MockMvc.
Usar o métodospringSecurity estático fornecido porSecurityMockMvcConfigurer é a maneira preferida de fazer isso:
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class SecuredControllerSpringBootIntegrationTest {
@Autowired
private WebApplicationContext context;
private MockMvc mvc;
@Before
public void setup() {
mvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(springSecurity())
.build();
}
// ... other methods
@WithMockUser("spring")
@Test
public void givenAuthRequestOnPrivateService_shouldSucceedWith200() throws Exception {
mvc.perform(get("/private/hello").contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
}
7. Testando métodos seguros com@SpringBootTest
@SpringBootTest não requer nenhuma configuração adicional para testar métodos seguros. Podemossimply call the methods directly and use @WithMockUser as needed:
@RunWith(SpringRunner.class)
@SpringBootTest
public class SecuredMethodSpringBootIntegrationTest {
@Autowired
private SecuredService service;
@Test(expected = AuthenticationCredentialsNotFoundException.class)
public void givenUnauthenticated_whenCallService_thenThrowsException() {
service.sayHelloSecured();
}
@WithMockUser(username="spring")
@Test
public void givenAuthenticated_whenCallServiceWithSecured_thenOk() {
assertThat(service.sayHelloSecured()).isNotBlank();
}
}
8. Testando com@SpringBootTest eTestRestTemplate
TestRestTemplate é uma opção conveniente ao escrever testes de integração para pontos de extremidade REST protegidos.
Podemossimply autowire a template and set credentials before requesting secured endpoints:
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class SecuredControllerRestTemplateIntegrationTest {
@Autowired
private TestRestTemplate template;
// ... other methods
@Test
public void givenAuthRequestOnPrivateService_shouldSucceedWith200() throws Exception {
ResponseEntity result = template.withBasicAuth("spring", "secret")
.getForEntity("/private/hello", String.class);
assertEquals(HttpStatus.OK, result.getStatusCode());
}
}
TestRestTemplate é flexível e oferece muitas opções úteis relacionadas à segurança. Para obter mais detalhes sobreTestRestTemplate, verifique nossoarticle on the topic.
9. Conclusão
Neste artigo, vimos várias maneiras de executar testes de integração habilitados para segurança.
Vimos como trabalhar com os pontos de extremidade mvccontroller e REST e também com métodos seguros.
Como de costume, todo o código-fonte para o exemplo aqui pode serfound over on GitHub.