Spring Boot Authentication for Integration Tests Spring Boot Authentication for Integration Tests spring spring

Spring Boot Authentication for Integration Tests


You can try excluding few more auto configurations:

@EnableAutoConfiguration(exclude = {org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration.class,org.springframework.boot.autoconfigure.security.SecurityFilterAutoConfiguration.class,org.springframework.boot.autoconfigure.security.FallbackWebSecurityAutoConfiguration.class,org.springframework.boot.autoconfigure.security.oauth2.OAuth2AutoConfiguration.class})

Btw, more elegant way of excluding stuff is by defining application-test.properties in your test sources and marking your test with @Profile("test"). Then just add this to your config:

spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration,org.springframework.boot.autoconfigure.security.SecurityFilterAutoConfiguration,org.springframework.boot.autoconfigure.security.FallbackWebSecurityAutoConfiguration,org.springframework.boot.autoconfigure.security.oauth2.OAuth2AutoConfiguration

All the possible configurations that can be excluded you can find here: spring.factories


You have couple of options to provide authentication in the spring boot integration test. You may need to adjust a few things to make it all work at your end.

Mock based approach

This uses test WebApplicationContext injected into MockMvc with @WithMockUser annotation to provide authentication user and WithMockUserSecurityContextFactory creating the security context for the mock user.

SecurityMockMvcConfigurers registers the security filter springSecurityFilterChain with MockMvc.

import org.junit.Before;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.http.MediaType;import org.springframework.security.test.context.support.WithMockUser;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;import org.springframework.test.web.servlet.MockMvc;import org.springframework.test.web.servlet.setup.MockMvcBuilders;import org.springframework.web.context.WebApplicationContext;import static org.hamcrest.Matchers.is;import static org.hamcrest.Matchers.notNullValue;import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;@RunWith(SpringRunner.class)@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)public class HelloControllerIT  {    @Autowired    private WebApplicationContext context;    private  MockMvc mvc;    @Before    public void setup() {        mvc = MockMvcBuilders                .webAppContextSetup(context)                .apply(springSecurity()) // enable security for the mock set up                .build();    }    @WithMockUser(value = "test", password = "pass")    @Test    public void test() throws Exception {        String contentType = MediaType.APPLICATION_JSON + ";charset=UTF-8";        String authzToken = mvc                .perform(                        post("/authenticate")                                .contentType(                                        MediaType.APPLICATION_JSON).                            content("")).                 andExpect(status().isOk())                .andExpect(content().contentType(contentType))                .andExpect(jsonPath("$.token", is(notNullValue())))                .andReturn().getResponse().getContentAsString();        System.out.print(authzToken);//{"token":"1a3434a"}    }}

In-memory auth provider based approach

This uses in-memory auth provider with basic authentication user.

Register in-memory auth provider and enable basic auth, disable anonymous access in HttpSecurity in the WebSecurityConfigurerAdapter.

When in-memory provider is registered, DefaultInMemoryUserDetailsManagerConfigurer creates the basic auth user in the memory.

When basic authentication is enabled, HttpBasicConfigurer configures BasicAuthenticationFilter. Authenticates the test user and creates the security context.

Security Configuration

@EnableWebSecurity@EnableWebMvc@EnableGlobalMethodSecurity(prePostEnabled = true)public class WebSecurityConfig extends WebSecurityConfigurerAdapter {    @Override    public void configure(AuthenticationManagerBuilder auth) throws Exception {                    // register test user with in memory authentication provider         auth.inMemoryAuthentication().withUser("test").password("pass").roles("ROLES");    }                @Override    public void configure(HttpSecurity http) throws Exception {                    // enable basic authentication & disable anoymous access        http.authorizeRequests().anyRequest().authenticated().and().httpBasic().and().anonymous().disable();        }}

Authentication Endpoint

@Controller@RequestMapping("/authenticate")public class AuthenticationController {    @RequestMapping(method = RequestMethod.POST)    @ResponseBody    public TokenClass getToken() {        TokenClass tokenClass = new TokenClass();        tokenClass.setToken("1a3434a");        return tokenClass;    }}

Pojo

public class TokenClass {    private String token;    public String getToken() {        return token;    }    public void setToken(String token) {        this.token = token;    }}

Test Controller

import com.fasterxml.jackson.databind.JsonNode;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.boot.test.web.client.TestRestTemplate;import org.springframework.http.*;import org.springframework.test.context.junit4.SpringRunner;import java.util.Arrays;import java.util.Base64;@RunWith(SpringRunner.class)@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)public class HelloControllerIT  {    @Autowired    private TestRestTemplate template;    @Test    public void test() throws Exception {        HttpHeaders authHeaders = new HttpHeaders();        String token = new String(Base64.getEncoder().encode(                ("test" + ":" + "pass").getBytes()));        authHeaders.set("Authorization", "Basic " + token);        JsonNode loginResponse = template.postForObject("/authenticate", new HttpEntity<>(null, authHeaders), JsonNode.class);        HttpHeaders authzHeaders = new HttpHeaders();        authzHeaders.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));        authzHeaders.add("X-Authorization", "Bearer " + loginResponse.get("token").textValue());        authzHeaders.add("Content-Type", "application/json");        ResponseEntity response = template.exchange("/secure",                HttpMethod.GET,                new HttpEntity<>(null, authzHeaders),                String.class        );    }}


It looks like default security configuration are getting kicked-in. Unless I see your your complete configuration it is hard to confirm this. If possible, could you post the your minimal project (on github?).

Since you do not want to enforce the authentication during executions of integration tests, you may enable the anonymous access of your application resources.

To enable anonymous access, you may add below class under your test source directory. It will configure the anonymous access during bootstrapping the web application. (should not see 401 response code)

@Configurationpublic class AllowAnonymousWebAccess extends WebSecurityConfigurerAdapter {    @Override    public void configure(HttpSecurity web) throws Exception {        web.antMatcher("**/*").anonymous();    }}