I use spring boot version "1.3.0.M5" (I also tried version "1.2.5.RELEASE"). I added spring security:
<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>
and code:
@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;
}
}
and test that is failing when /api/sampleentity is access: 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;
}
}
When I run application from main() and access url: http://localhost:8080/api/sampleentity I am redirected to login page.
How can I run my test and disable security or just log in user ?
--my solution: exclude security from the test using profiles:
@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}
enabled=false and management. security. enabled=false should be set to disable the security.
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:
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.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.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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With