Spring OAuth и тест интеграции загрузки

Как лучше всего запускать интеграционные тесты Spring Boot с помощью веб-приложения, настроенного на сервере OAuth Resource.

Я могу думать о двух теоретических подходах:

  1. Смоделируйте контекст безопасности на сервере ресурсов, не вызывая сервера авторизации.
  2. Вставьте сервер авторизации как часть теста и перенаправьте на него аутентификацию.

Мне было интересно, как другие подошли к этой проблеме.

2 ответа

Решение

Я использую Spring Security 4.x @WithSecurityContext('user') аннотация для создания макета SecurityContext с 'user' вошел в систему. Затем при вызове моего REST API с помощью MockMvc Я получаю SecurityContext и прикрепить его к звонку.

Как это:

@Test
@Transactional
@WithSecurityContext('user')
public void getAllParcels() throws Exception {
    // Initialize the database

    long size = parcelRepository.count();
    parcelRepository.saveAndFlush(parcel);

    // Get all the parcels
    restParcelMockMvc.perform(get("/api/parcels").with(security()))
        .andExpect(status().isOk())
        .andExpect(content().contentType(MediaType.APPLICATION_JSON))
        .andExpect(jsonPath("$.[" + size + "].id").value(parcel.getId()))
        .andExpect(jsonPath("$.[" + size + "].lot").value(DEFAULT_LOT))
        .andExpect(jsonPath("$.[" + size + "].localName").value(DEFAULT_LOCAL_NAME));
}

где security() это статический метод:

public static RequestPostProcessor security() {
        return SecurityMockMvcRequestPostProcessors.securityContext(SecurityContextHolder.getContext());
}

Итак, используя @WithSecurityContext('user') издеваться SecurityContext с авторизованным пользователем с логином 'user' создан для моего метода испытаний. Затем в этом методе я получаю этот макет SecurityContext и присоедините его к вызову REST API, чтобы мой oAuth думал, что пользователь уже аутентифицирован. Это в основном первый подход, который вы предложили в своем вопросе.

Чтобы это работало, вы должны поменять свой OAuth на состояние для тестов. Иначе это не сработает.

т.е. вот так:

@Configuration
public class OAuth2ServerConfiguration {

    @Configuration
    @EnableResourceServer
    protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {

        @Autowired(required = false)
        @Qualifier("oauth2StatelessSecurityContext")
        private Boolean stateless = Boolean.TRUE; // STATEFUL switching for tests!

        @Inject
        private Http401UnauthorizedEntryPoint authenticationEntryPoint;

        @Inject
        private AjaxLogoutSuccessHandler ajaxLogoutSuccessHandler;

        @Override
        public void configure(HttpSecurity http) throws Exception {
            http
                .exceptionHandling()
                .authenticationEntryPoint(authenticationEntryPoint)
                .and()
                .logout()
                .logoutUrl("/api/logout")
                .logoutSuccessHandler(ajaxLogoutSuccessHandler)
                .and()
                .csrf()
                .requireCsrfProtectionMatcher(new AntPathRequestMatcher("/oauth/authorize"))
                .disable()
                .headers()
                .frameOptions().disable().and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                .antMatchers("/api/authenticate").permitAll()
                .antMatchers("/api/register").permitAll()
                .antMatchers("/api/logs/**").hasAnyAuthority(AuthoritiesConstants.ADMIN)
                .antMatchers("/api/**").authenticated()
                .antMatchers("/metrics/**").hasAuthority(AuthoritiesConstants.ADMIN)
                .antMatchers("/health/**").hasAuthority(AuthoritiesConstants.ADMIN)
                .antMatchers("/trace/**").hasAuthority(AuthoritiesConstants.ADMIN)
                .antMatchers("/dump/**").hasAuthority(AuthoritiesConstants.ADMIN)
                .antMatchers("/shutdown/**").hasAuthority(AuthoritiesConstants.ADMIN)
                .antMatchers("/beans/**").hasAuthority(AuthoritiesConstants.ADMIN)
                .antMatchers("/configprops/**").hasAuthority(AuthoritiesConstants.ADMIN)
                .antMatchers("/info/**").hasAuthority(AuthoritiesConstants.ADMIN)
                .antMatchers("/autoconfig/**").hasAuthority(AuthoritiesConstants.ADMIN)
                .antMatchers("/env/**").hasAuthority(AuthoritiesConstants.ADMIN)
                .antMatchers("/trace/**").hasAuthority(AuthoritiesConstants.ADMIN)
                .antMatchers("/api-docs/**").hasAuthority(AuthoritiesConstants.ADMIN)
                .antMatchers("/protected/**").authenticated();
        }

        @Override
        public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
            resources.stateless(stateless);
            super.configure(resources);
        }

    }
...

Вы видите мой stateless свойство, которое вводится только в тестах. В обычном режиме он использует значение по умолчанию true (так что это без гражданства). Для тестов заявляю oauth2StatelessSecurityContext Бин со значением false так что получается Statefull для испытаний.

Я определяю эту конфигурацию для тестов:

@Configuration
public class OAuth2Statefull {

    @Bean
    @Primary       
    public Boolean oauth2StatelessSecurityContext() {
        return Boolean.FALSE;
    }

}

Вот как я это сделал. Я надеюсь, что мое объяснение понятно.

Этот ответ очень похож на ответ, предоставленный Ондреем, но немного проще.

Spring Security 4 обеспечивает тестовую поддержку. Чтобы использовать его, убедитесь, что у вас есть spring-security-test-4.0.2.RELEASE.jar (или более новая версия в вашем classpath). Вы также захотите убедиться, что работаете с spring-test-4.1.0.RELEASE (или новее).

Далее вы можете использовать MockMvc, как показывает другой ответ. Однако, если вы настроите MockMvc со следующим:

import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@WebAppConfiguration
public class OAuthTests {

    @Autowired
    private WebApplicationContext context;

    private MockMvc mvc;

    @Before
    public void setup() {
        mvc = MockMvcBuilders
                .webAppContextSetup(context)

                // ADD THIS!!
                .apply(springSecurity())
                .build();
    }

Это делает это так

  • Вам больше не нужно беспокоиться о работе в режиме без состояния или нет
  • Это также означает, что вам не нужно использовать apply(springSecurity()), как указано в другом ответе.

Короче говоря, вы должны быть в состоянии сделать что-то вроде этого:

@Test
@WithSecurityContext('user')
public void performOAuth() throws Exception {
    ...    
    // No need for apply(security())!!
    restParcelMockMvc.perform(get("/api/some-resource"))
        .andExpect(...);
}

Я бы посоветовал вам прочитать оставшуюся часть справочного раздела "Тестирование безопасности в Spring", поскольку в нем содержится много дополнительных сведений, в том числе о том, как использовать пользовательскую аутентификацию.

Другие вопросы по тегам