Spring boot, disable security for tests

后端 未结 2 1495
攒了一身酷
攒了一身酷 2021-02-20 08:22

I use spring boot version \"1.3.0.M5\" (I also tried version \"1.2.5.RELEASE\"). I added spring security:


  org.springframework         


        
相关标签:
2条回答
  • 2021-02-20 08:29

    You have to do some changes to your config and test to solve your problem(s).

    First I'll explain why your solution isn't working:

    1. The Spring RestTemplate class is a possible way to access your REST service but lacks some header informations the way it is constructed (Which doesn't mean it's impossible with the RestTemplate). Thats why the authentication didn't work.
    2. My first solution attempt isn't working because of the usage of the RestTemplate class, as the RestTemplate request is likely to create a new session. It sets an entirely different environment. My code works if you want to test Methods secured with the @PreAuthorize annotation but only if you want to execute such a method directly in your test and you need a valid authentication.
    3. You can't automatically authorize any user as of your current spring security configuration.

    Second, here are the necessary changes to your code:

    First the configuration class

    @Configuration
    @EnableWebSecurity
    public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
    
    @Override
      protected void configure(AuthenticationManagerBuilder auth) throws Exception {
      auth.inMemoryAuthentication().withUser("user").password("password").roles("USER" );
      }
    
      @Override
      protected void configure(HttpSecurity http) throws Exception {
        http.httpBasic().and().csrf().disable()
        .authorizeRequests().antMatchers("/api/sampleentity").authenticated()
        .and().authorizeRequests().antMatchers("/users").hasRole("ADMIN")
        .and().formLogin().permitAll()
        .and().logout().permitAll().logoutUrl("/logout")
        .logoutSuccessUrl("/");
      }
    
      @Override
      @Bean
      public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
      }
    }
    

    I had to add httpBasic Authentication support (to enable authentication via http header attribute) and I disabled csrf tokens (the latter just for convenience, you should reenable them according to criticality of your application).

    And second the Testclass:

    import java.io.IOException;
    import java.nio.charset.Charset;
    import java.util.Arrays;
    
    import javax.servlet.Filter;
    
    import org.junit.Assert;
    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.IntegrationTest;
    import org.springframework.boot.test.SpringApplicationConfiguration;
    import org.springframework.http.MediaType;
    import org.springframework.http.converter.HttpMessageConverter;
    import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
    import org.springframework.mock.http.MockHttpOutputMessage;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    import org.springframework.test.context.web.WebAppConfiguration;
    import org.springframework.test.web.servlet.MockMvc;
    import org.springframework.test.web.servlet.setup.MockMvcBuilders;
    import org.springframework.web.context.WebApplicationContext;
    
    import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
    import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
    import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
    import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*;
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringApplicationConfiguration(classes = SpringBootMainApplication.class)
    @WebAppConfiguration
    @IntegrationTest({ "server.port=0" })
    public class SampleEntityTest {
    
    private String url;
    private MockMvc mockMvc;
    private HttpMessageConverter mappingJackson2HttpMessageConverter;
    
    private MediaType contentType = new MediaType(
            MediaType.APPLICATION_JSON.getType(),
            MediaType.APPLICATION_JSON.getSubtype(), Charset.forName("utf8"));
    
    @Autowired
    private WebApplicationContext webApplicationContext;
    
    @Autowired
    private Filter springSecurityFilterChain;
    
    @Autowired
    void setConverters(HttpMessageConverter<?>[] converters) {
        for (HttpMessageConverter hmc : Arrays.asList(converters)) {
            if (hmc instanceof MappingJackson2HttpMessageConverter) {
                this.mappingJackson2HttpMessageConverter = hmc;
            }
        }
    
        Assert.assertNotNull("the JSON message converter must not be null",
                this.mappingJackson2HttpMessageConverter);
    }
    
    @Before
    public void setUp() {
        url = "/api/sampleentity";
        mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
                .addFilters(springSecurityFilterChain).build();
    }
    
    @Test
    public void testEntityGet() throws Exception {
        mockMvc.perform(
                get(url)
                .with(httpBasic("user", "password")))
                .andExpect(status().isOk());
    }
    
    @Test
    public void testEntityPost() throws Exception {
        SampleEntity sampleEntity = new SampleEntity();
        sampleEntity.setName("name");
        sampleEntity.setId(1);
        String json = json(sampleEntity);
        mockMvc.perform(
                post(url)
                .contentType(contentType)
                .content(json)
                .with(httpBasic("user", "password")))
                .andExpect(status().isCreated());
    }
    
    protected String json(Object o) throws IOException {
        MockHttpOutputMessage mockHttpOutputMessage = new MockHttpOutputMessage();
        this.mappingJackson2HttpMessageConverter.write(o,
                MediaType.APPLICATION_JSON, mockHttpOutputMessage);
        return mockHttpOutputMessage.getBodyAsString();
    }
    

    }

    I have used the spring/ spring security test approach here.

    Versions used:

        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>1.2.5.RELEASE</version>
        </parent>
    
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-core</artifactId>
            <version>4.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-web</artifactId>
            <version>4.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-config</artifactId>
            <version>4.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <version>4.0.2.RELEASE</version>
            <scope>test</scope>
        </dependency>
    

    If you want to test your rest api i can recommend you the Postman plugin for Chrome. As that can help you identify the problem much faster.

    I hope this helps you to finally solve your problem.

    0 讨论(0)
  • 2021-02-20 08:35

    If you want to see what's being auto-configured, launch your web app and access the autoconfig endpoint (e.g., http://localhost:8080/autoconfig). Then search for 'Security' to see which 'AutoConfiguration' classes are being detected.

    You can then disable auto-configuration of security by excluding those classes like this:

    @EnableAutoConfiguration(exclude = { SecurityAutoConfiguration.class, ManagementSecurityAutoConfiguration.class })
    

    Of course, you won't want to exclude them for production deployments. Thus you'll need to have a separate @Configuration class for production and tests.

    Or if you want a detailed answer go for below-mentioned steps


    Add annotation @Profile(value = {"development", "production"}) to my implementation of WebSecurityConfigurerAdapter

    @Configuration
        @EnableWebSecurity
        @Profile(value = {"development", "production"})
        public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    

    Now, in test/resources, create application-test.yml to define properties for test profile and add this -

    # Security enable/disable
    security:
      basic:
        enabled: false
    

    Now, to your test cases, add this annotation to apply the active profile @ActiveProfiles(value = "test"). This is how my class looked -

    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringApplicationConfiguration(classes = Application.class)
    @WebAppConfiguration
    @ActiveProfiles(value = "test")
    @IntegrationTest({"server.port=0"})
    public class SampleControllerIntegrationTest {
    

    Doing this will disabled security for tests. Best of luck!!!

    0 讨论(0)
提交回复
热议问题