Spring OAuth и тест интеграции загрузки
Как лучше всего запускать интеграционные тесты Spring Boot с помощью веб-приложения, настроенного на сервере OAuth Resource.
Я могу думать о двух теоретических подходах:
- Смоделируйте контекст безопасности на сервере ресурсов, не вызывая сервера авторизации.
- Вставьте сервер авторизации как часть теста и перенаправьте на него аутентификацию.
Мне было интересно, как другие подошли к этой проблеме.
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", поскольку в нем содержится много дополнительных сведений, в том числе о том, как использовать пользовательскую аутентификацию.