一尘不染

Spring OAuth和启动集成测试

spring-boot

运行Spring Boot集成测试的最佳方法是再次配置OAuth Resource服务器的Web应用程序。

我可以想到两种理论方法:

  1. 在资源服务器中模拟安全上下文,而无需手动调用授权服务器。
  2. 将授权服务器作为测试的一部分嵌入,并将身份验证重定向到该服务器。

我想知道其他人如何解决这个问题。


阅读 270

收藏
2020-05-30

共1个答案

一尘不染

我使用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')模拟对象。然后在该方法中,我检索此模拟并将其附加到REST
API调用,以使我的oAuth认为用户已通过身份验证。基本上,这是您在问题中建议的第一种方法。SecurityContext``'user'``SecurityContext

为此,您必须将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用值声明Bean,false因此它对于测试变为有状态。

我为测试定义此配置:

@Configuration
public class OAuth2Statefull {

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

}

那就是我做到的。我希望我的解释是可以理解的。

2020-05-30