I use spring boot version \"1.3.0.M5\" (I also tried version \"1.2.5.RELEASE\"). I added spring security:
org.springframework
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:
org.springframework.boot
spring-boot-starter-parent
1.2.5.RELEASE
org.springframework.security
spring-security-core
4.0.2.RELEASE
org.springframework.security
spring-security-web
4.0.2.RELEASE
org.springframework.security
spring-security-config
4.0.2.RELEASE
org.springframework.security
spring-security-test
4.0.2.RELEASE
test
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.