2015-10-05 101 views
8

我使用弹簧启动版本“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禁止(.. 。)

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

当我运行从主应用程序()和访问网址: http://localhost:8080/api/sampleentity 我重定向到登录页面。

如何运行我的测试并禁用安全性或仅登录用户?

- 我的解决方案:从使用配置文件测试排除安全:

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

回答

2

你必须做一些改变你的配置和测试来解决问题(一个或多个)。

首先,我将解释为什么你的解决方案是行不通的:

  1. 春季RestTemplate类是访问您的REST服务一个可行的办法,但缺乏一些头信息的建造方式(这没有按”这意味着它不可能与RestTemplate)。这就是为什么认证不起作用。
  2. 我的第一个解决方案尝试不起作用,因为RestTemplate类的用法,因为RestTemplate请求可能会创建一个新的会话。它设定了完全不同的环境。如果您要测试使用@PreAuthorize批注保护的方法,但是只有在您想要直接在您的测试中执行此类方法并且您需要有效的认证时,我的代码才有效。
  3. 对于当前的弹簧安全配置,您无法自动授权任何用户。

其次,这里有你的代码进行必要的修改:

首先配置类

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

如果你想测试你的REST API我可以推荐你的邮差插件的Chrome。因为这可以帮助您更快地发现问题。

我希望这可以帮助你最终解决你的问题。

+0

我更新了我的帖子。我尝试了你的建议,但我仍然有403禁止。我做错了什么? –

+0

您可以在没有@WithMockUser的情况下尝试吗?如果你有自定义的实现,你能提供你的AuthenticationProvider吗? – BeWu

+0

嗨,这3个关系是分开制作的(所以我评论@WithMockUser)。我不使用任何自定义AuthenticationProvider。 –