夢のの夢
夢のの夢

Reputation: 5826

Spring Security @WithMockUser doesn't seem to work with state changing verbs (post, put..)

Here is my setup:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .authorizeRequests()
            .antMatchers("/signup", "/health").permitAll()
            .anyRequest().authenticated().and()
        .formLogin()
            .loginPage("/login")
            .permitAll()
...

Test class:

@ExtendWith(SpringExtension.class)
@WebMvcTest
@WithMockUser
class ApiControllerTest {
    ...

@WithMockUser works fine with below GET:

mockMvc.perform(get("/api/book/{id}", id))
        .andExpect(status().isOk())
...

but not with POST:

mockMvc.perform(post("/api/book")
        .contentType(MediaType.APPLICATION_JSON)
        .content(payload))
        .andExpect(status().isCreated())
...

When I look into the logs for MockHttpServletResponse, I notice the response is giving a redirect to login page, as follows:

MockHttpServletResponse:
           Status = 302
    Error message = null
          Headers = [X-Content-Type-Options:"nosniff", X-XSS-Protection:"1; mode=block", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Pragma:"no-cache", Expires:"0", X-Frame-Options:"DENY", Location:"/login"]
     Content type = null
             Body = 
    Forwarded URL = null
   Redirected URL = /login
          Cookies = []

I know that @WithMockUser provides good amount of defaults for mocking user authentication. Why it is not working for stateful API request?

Upvotes: 3

Views: 2151

Answers (1)

Dirk Deyne
Dirk Deyne

Reputation: 6936

By default, Spring Security protects you against Cross-site request forgery.

You have to actively disable it in your configuration if you don't want it.

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.csrf().disable()
        .authorizeRequests()
        ...

Luckily you did not, it is not safe to do so.

But as a consequence, you need to provide a csrf-token every time you do a POST, so also in your tests!


import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;

...

mockMvc.perform(post("/api/book")
       .with(csrf()) // <--- missing in your test
       .contentType(MediaType.APPLICATION_JSON)
       .content(payload))
       .andExpect(status().isCreated());

Now your test should work.

Upvotes: 10

Related Questions