Spring Security @WithMockUser 似乎不适用于状态更改动词(发布,放置..)

夢のの*_*のの夢 1 spring-mvc spring-security spring-boot

这是我的设置:

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

Run Code Online (Sandbox Code Playgroud)

测试类:

@ExtendWith(SpringExtension.class)
@WebMvcTest
@WithMockUser
class ApiControllerTest {
    ...
Run Code Online (Sandbox Code Playgroud)

@WithMockUser 使用以下 GET 可以正常工作:

mockMvc.perform(get("/api/book/{id}", id))
        .andExpect(status().isOk())
...
Run Code Online (Sandbox Code Playgroud)

但不是 POST:

mockMvc.perform(post("/api/book")
        .contentType(MediaType.APPLICATION_JSON)
        .content(payload))
        .andExpect(status().isCreated())
...
Run Code Online (Sandbox Code Playgroud)

当我查看 MockHttpServletResponse 的日志时,我注意到响应重定向到登录页面,如下所示:

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 = []
Run Code Online (Sandbox Code Playgroud)

我知道这@WithMockUser为模拟用户身份验证提供了大量的默认值。为什么它不适用于有状态的 API 请求?

Dir*_*yne 5

默认情况下,Spring Security 保护您免受跨站点请求伪造。

如果你不想要它,你必须在你的配置中主动禁用它。

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.csrf().disable()
        .authorizeRequests()
        ...
Run Code Online (Sandbox Code Playgroud)

幸运的是你没有这样做,这样做是不安全的。

但因此,您每次执行 a 时都需要提供 csrf 令牌POST,在您的测试中也是如此!


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

Run Code Online (Sandbox Code Playgroud)

现在你的测试应该可以工作了。

  • 你救了我的命!谢谢你!有时你无法想象这样一个简短的答案可以节省多少时间...... (2认同)