簡體   English   中英

Spring啟動,禁用測試安全性

[英]Spring boot, disable security for tests

我使用spring boot version“1.3.0.M5”(我也試過版本“1.2.5.RELEASE”)。 我添加了彈簧安全性:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.security</groupId>
  <artifactId>spring-security-test</artifactId>
  <scope>test</scope>
</dependency>

和代碼:

@SpringBootApplication
public class SpringBootMainApplication {
  public static void main(String[] args) {
    SpringApplication.run(SpringBootMainApplication.class, args);
  }
}

@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.authorizeRequests()
    .antMatchers("/api/sampleentity").authenticated()
    .and().authorizeRequests()
    .and().formLogin().permitAll()
    .and().logout().permitAll().logoutUrl("/logout")
    .logoutSuccessUrl("/");
  }
  @Override
  @Bean
  public AuthenticationManager authenticationManagerBean() throws Exception {
    return super.authenticationManagerBean();
  }
}

@RestController
@RequestMapping("/api/sampleentity")
public class SampleEntityController {
  @RequestMapping(method= RequestMethod.GET)
  public Iterable<SampleEntity> getAll() {
    return ImmutableSet.of();
  }
  @RequestMapping(method=RequestMethod.POST)
  @ResponseStatus(value= HttpStatus.CREATED)
  public SampleEntity create(@RequestBody SampleEntity sampleEntity) {
    return sampleEntity;
  }
}

/ api / sampleentity訪問時失敗的測試:org.springframework.web.client.HttpClientErrorException:403 Forbidden(...)

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = SpringBootMainApplication.class)
@WebAppConfiguration
@IntegrationTest({"server.port=0"})
public class SampleEntityTest {
  @Value("${local.server.port}")
  private int port;
  private String url;
  private RestTemplate restTemplate;
  @Autowired
  private ApplicationContext context;
  @BeforeClass
  public static void authenticate(){
//ONE TRY
//        Authentication authentication =
//                new UsernamePasswordAuthenticationToken("user", "password",
//                                                        AuthorityUtils.createAuthorityList("USER")); //tried "ROLE_USER"
//        SecurityContextHolder.getContext().setAuthentication(authentication);
  }
  @Before
  public void setUp() {
    url = String.format("http://localhost:%s/api/sampleentity", port);
    restTemplate = new RestTemplate();
//ANOTHER TRY
//        AuthenticationManager authenticationManager = context.getBean(AuthenticationManager.class);
//        Authentication authentication = authenticationManager
//                .authenticate(new UsernamePasswordAuthenticationToken("user", "password", AuthorityUtils.createAuthorityList("USER"))); //tried "ROLE_USER"
//        SecurityContextHolder.getContext().setAuthentication(authentication);
  }
  //THIS METHOD SHOULD WORK !
  @Test
//ANOTHER TRY
//@WithMockUser(username="user",password = "password", roles={"USER"})//tried "ROLE_USER"
  public void testEntity_create() throws Exception {
    SampleEntity sampleEntity = create("name", 1);
    ResponseEntity<SampleEntity> response = restTemplate.postForEntity(url, sampleEntity, SampleEntity.class);
    assertEquals(HttpStatus.CREATED, response.getStatusCode());
  }
  private SampleEntity create(String name, int id) {
    SampleEntity entity = new SampleEntity();
    entity.setName(name);
    entity.setId(id);
    return entity;
  }
}

當我從main()運行應用程序並訪問url時: http:// localhost:8080 / api / sampleentity我被重定向到登錄頁面。

如何運行測試並禁用安全性或只登錄用戶?

--my solution:使用配置文件從測試中排除安全性:

@SpringBootApplication
@EnableAutoConfiguration(exclude = { SecurityAutoConfiguration.class})
public class SpringBootMainApplication {body the same}

@EnableWebSecurity
@Import(SecurityAutoConfiguration.class)
@Profile("!test")
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {body the same}

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = SpringBootMainApplication.class)
@WebAppConfiguration
@IntegrationTest({"server.port=0"})
@ActiveProfiles("test")
public class SampleEntityTest {body the same}

您必須對配置進行一些更改並測試以解決您的問題。

首先,我將解釋為什么您的解決方案不起作用:

  1. Spring RestTemplate類是一種訪問REST服務的可能方式,但缺少一些構造方式的頭信息(這並不意味着RestTemplate不可能)。 這就是為什么身份驗證不起作用。
  2. 由於RestTemplate類的使用,我的第一個解決方案嘗試RestTemplate ,因為RestTemplate請求可能會創建一個新會話。 它創造了一個完全不同的環境。 如果您想測試使用@PreAuthorize注釋保護的方法,但僅當您想要在測試中直接執行此類方法並且需要有效的身份驗證時,我的代碼才有效。
  3. 從當前的Spring安全配置開始,您無法自動授權任何用戶。

其次,以下是對代碼的必要更改:

首先是配置類

@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();
  }
}

我不得不添加httpBasic身份驗證支持(通過http頭屬性啟用身份驗證),我禁用了csrf令牌(后者只是為了方便,你應該根據應用程序的關鍵性重新啟用它們)。

第二個是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();
}

}

我在這里使用了彈簧/彈簧安全測試方法。

使用的版本:

    <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>

如果你想測試你的休息api,我可以推薦你的Chrome郵差插件。 因為這可以幫助您更快地識別問題。

我希望這可以幫助您最終解決您的問題。

如果要查看自動配置的內容,請啟動Web應用程序並訪問autoconfig端點(例如, http:// localhost:8080 / autoconfig )。 然后搜索“ Security ”以查看正在檢測哪些“ AutoConfiguration ”類。

然后,您可以通過排除這樣的類來禁用安全性的自動配置:

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

當然,您不希望將它們排除在生產部署之外。 因此,您需要為生產和測試提供單獨的@Configuration類。

或者,如果您想要詳細的答案,請參閱下面提到的步驟


將注釋@Profile(value = {"development", "production"})到我的WebSecurityConfigurerAdapter實現中

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

現在,在test / resources中,創建application-test.yml來定義測試配置文件的屬性並添加 -

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

現在,對於您的測試用例,添加此注釋以應用活動配置文件@ActiveProfiles(value =“test”)。 這就是我班上的樣子 -

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

這樣做會禁用測試的安全性。 祝你好運!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM