I use spring security 4.x @WithSecurityContext('user')
annotation to create mock SecurityContext
with 'user'
logged in. Then when calling my REST API using MockMvc
I retrieve SecurityContext
and attach it to the call.
Like this:
@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));
}
where security()
is static method:
public static RequestPostProcessor security() {
return SecurityMockMvcRequestPostProcessors.securityContext(SecurityContextHolder.getContext());
}
So using @WithSecurityContext('user')
mock SecurityContext
with authenticated user with login 'user'
is created for my test method. Then in that method I retrieve this mock SecurityContext
and attach it to the REST API call to make my oAuth think user is allready authenticated. It's basically the first approach you suggested in your question.
For this to work you must switch your OAuth to be statefull for the tests. Otherwise it won't work.
ie like this:
@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);
}
}
...
You see my stateless
property which gets injected only in tests. In normal run it uses it's default value true
(so it's stateless). For tests I declare oauth2StatelessSecurityContext
Bean with value false
so it turns statefull for tests.
I define this configuration for tests:
@Configuration
public class OAuth2Statefull {
@Bean
@Primary
public Boolean oauth2StatelessSecurityContext() {
return Boolean.FALSE;
}
}
That's how I did it. I hope my explanation is understandable.