Spring Security для тестов интеграции Spring Boot

Spring Security для тестов интеграции Spring Boot

1. Вступление

Возможность выполнения интеграционных тестов без необходимости в отдельной среде интеграции является ценной функцией для любого программного стека. Плавная интеграция Spring Boot с Spring Security упрощает тестирование компонентов, взаимодействующих с уровнем безопасности.

В этом кратком руководстве мы рассмотрим использование@MockMvcTest и@SpringBootTest для выполнения интеграционных тестов безопасности.

2. зависимости

Давайте сначала внесем зависимости, которые нам понадобятся для нашего примера:


    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

Стартерыspring-boot-starter-webspring-boot-starter-security, иspring-boot-starter-test предоставляют нам доступ к утилитам Spring MVC, Spring Security и Spring Boot test.

Кроме того, мы добавимspring-security-test, чтобы получить доступ к аннотации@WithMockUser, которую мы будем использовать.

3. Конфигурация веб-безопасности

Наша настройка веб-безопасности будет простой. Только аутентифицированные пользователи смогут получить доступ к путям, которые соответствуют/private/. Пути, соответствующие/public/, будут доступны любому пользователю:

@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. Конфигурация безопасности метода

В дополнение к безопасности на основе URL-пути, которую мы определили в нашихWebSecurityConfigurer,, мы можем настроить безопасность на основе методов, предоставив дополнительный файл конфигурации:

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfigurer
  extends GlobalMethodSecurityConfiguration {
}

Эта конфигурация включает поддержку аннотаций до и после Spring Security. Другие атрибуты также доступны, если требуется дополнительная поддержка. Для получения дополнительной информации о безопасности метода Spring взгляните на нашarticle on the topic.

5. Тестирование контроллеров с@WebMvcTest

При использовании подхода аннотации@WebMvcTest с Spring Security,MockMvc is automatically configured with the necessary filter chain  требуется для проверки нашей конфигурации безопасности.

ПосколькуMockMvc настроен для нас, мы можем использовать@WithMockUser для наших тестов без какой-либо дополнительной настройки:

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

Обратите внимание, что использование@WebMvcTest укажет Spring Boot создать экземпляр только веб-уровня, а не всего контекста. Из-за этогоcontroller tests that use @WebMvcTest will run faster than with other approaches.

6. Тестирование контроллеров с@SpringBootTest

При использовании аннотации@SpringBootTest для тестирования контроллеров с помощью Spring Securityit’s necessary to explicitly configure the filter chain when setting up MockMvc.

Использование статического методаspringSecurity, предоставляемогоSecurityMockMvcConfigurer, является предпочтительным способом сделать это:

@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. Тестирование защищенных методов с помощью@SpringBootTest

@SpringBootTest не требует дополнительной настройки для тестирования защищенных методов. Мы можемsimply 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. Тестирование с@SpringBootTest иTestRestTemplate

TestRestTemplate - удобный вариант при написании интеграционных тестов для защищенных конечных точек REST.

Мы можемsimply 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 гибок и предлагает множество полезных опций, связанных с безопасностью. Чтобы узнать больше оTestRestTemplate, ознакомьтесь с нашимarticle on the topic.

9. Заключение

В этой статье мы рассмотрели несколько способов выполнения интеграционных тестов с включенной безопасностью.

Мы рассмотрели, как работать с конечными точками mvccontroller и REST, а также с защищенными методами.

Как обычно, весь исходный код для примера здесь может бытьfound over on GitHub.