Use @WithMockUser (with @SpringBootTest) inside oAuth2 resource server application

Environment : I have an application for microservice architecture with spring boot, consisting of several infrastructure services and resources (containing business logic). Authorization and authentication are handled by the oAuth2-Service, which manages user objects and the creation of JWT tokens for clients.

To test one microservice application as a whole, I tried to build tests using testNG, spring.boot.test, org.springframework.security.test ...

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK, properties = {"spring.cloud.discovery.enabled=false", "spring.cloud.config.enabled=false", "spring.profiles.active=test"}) @AutoConfigureMockMvc @Test public class ArtistControllerTest extends AbstractTestNGSpringContextTests { @Autowired private MockMvc mvc; @BeforeClass @Transactional public void setUp() { // nothing to do } @AfterClass @Transactional public void tearDown() { // nothing to do here } @Test @WithMockUser(authorities = {"READ", "WRITE"}) public void getAllTest() throws Exception { // EXPECT HTTP STATUS 200 // BUT GET 401 this.mvc.perform(get("/") .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) } } 

where the security configuration (resource server) is as follows

 @Configuration @EnableResourceServer public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter { // get the configured token store @Autowired TokenStore tokenStore; // get the configured token converter @Autowired JwtAccessTokenConverter tokenConverter; /** * !!! configuration of springs http security !!! */ @Override public void configure(HttpSecurity http) throws Exception { http .csrf().disable() .authorizeRequests() .antMatchers("/**").authenticated(); } /** * configuration of springs resource server security */ @Override public void configure(ResourceServerSecurityConfigurer resources) throws Exception { // set the configured tokenStore to this resourceServer resources.resourceId("artist").tokenStore(tokenStore); } } 

and the next method-based security check annotated inside the controller class

 @PreAuthorize("hasAuthority('READ')") @RequestMapping(value = "/", method = RequestMethod.GET) public List<Foo> getAll(Principal user) { List<Foo> foos = fooRepository.findAll(); return foos; } 

I thought this would work, but when I run the test I get an assertion error

 java.lang.AssertionError: Status Expected :200 Actual :401 


Question : Is there something completely obvious that I'm doing wrong? Or is @WithMockUser not going to work with @SpringBootTest and @AutoConfigureMockMvc in oAuth2? If so ... what would be the best approach for testing security configurations based on routes and methods as part of such an (integration) test like this?


Appendix : I also tried different approaches like something like the following ... but this led to the same result :(

 this.mvc.perform(get("/") .with(user("admin").roles("READ","WRITE").authorities(() -> "READ", () -> "WRITE")) .accept(MediaType.APPLICATION_JSON)) 

see :
spring security testing
spring download 1.4 for testing

+12
source share
3 answers

@WithMockUser creates authentication in SecurityContext. The same goes for with(user("username")) .

By default, OAuth2AuthenticationProcessingFilter does not use SecurityContext, but always creates authentication from a token (without saving state).

You can easily change this behavior by setting the state security flag in the resource server security configuration to false:

 @Configuration @EnableResourceServer public class ResourceServerConfiguration implements ResourceServerConfigurer { @Override public void configure(ResourceServerSecurityConfigurer security) throws Exception { security.stateless(false); } @Override public void configure(HttpSecurity http) {} } 

Another option is the ResourceServerConfigurerAdapter extension, but the problem is that it comes with a configuration that forces all requests to authenticate. The implementation of the interface leaves your basic security configuration unchanged except for stateless state.

Of course, set the flag to false only in your test contexts.

+17
source

I had the same problem and the only way I found is to create a token and use it in mockMvc

 mockMvc.perform(get("/resource") .with(oAuthHelper.bearerToken("test")) 

And OAuthHelper:

 @Component @EnableAuthorizationServer public class OAuthHelper extends AuthorizationServerConfigurerAdapter { @Autowired AuthorizationServerTokenServices tokenservice; @Autowired ClientDetailsService clientDetailsService; public RequestPostProcessor bearerToken(final String clientid) { return mockRequest -> { OAuth2AccessToken token = createAccessToken(clientid); mockRequest.addHeader("Authorization", "Bearer " + token.getValue()); return mockRequest; }; } OAuth2AccessToken createAccessToken(final String clientId) { ClientDetails client = clientDetailsService.loadClientByClientId(clientId); Collection<GrantedAuthority> authorities = client.getAuthorities(); Set<String> resourceIds = client.getResourceIds(); Set<String> scopes = client.getScope(); Map<String, String> requestParameters = Collections.emptyMap(); boolean approved = true; String redirectUrl = null; Set<String> responseTypes = Collections.emptySet(); Map<String, Serializable> extensionProperties = Collections.emptyMap(); OAuth2Request oAuth2Request = new OAuth2Request(requestParameters, clientId, authorities, approved, scopes, resourceIds, redirectUrl, responseTypes, extensionProperties); User userPrincipal = new User("user", "", true, true, true, true, authorities); UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userPrincipal, null, authorities); OAuth2Authentication auth = new OAuth2Authentication(oAuth2Request, authenticationToken); return tokenservice.createAccessToken(auth); } @Override public void configure(final ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient("test") .authorities("READ"); } } 
+3
source

Since I specifically tried to write tests for our ResourceServerConfiguration, I circumvented this problem by creating a test wrapper for it, which set security.stateless to false:

 @Configuration @EnableResourceServer public class ResourceServerTestConfiguration extends ResourceServerConfigurerAdapter { private ResourceServerConfiguration configuration; public ResourceServerTestConfiguration(ResourceServerConfiguration configuration) { this.configuration = configuration; } @Override public void configure(ResourceServerSecurityConfigurer security) throws Exception { configuration.configure(security); security.stateless(false); } @Override public void configure(HttpSecurity http) throws Exception { configuration.configure(http); } } 
0
source

Source: https://habr.com/ru/post/1263243/


All Articles