在Spring安全性中启用CSRF保护的单元测试控制器

时间:2014-09-01 11:57:50

标签: spring-mvc junit spring-security csrf

最近我们为使用spring security 3.2的项目引入了CSRF保护。

启用CSRF后,由于请求中不存在csrf令牌,因此某些单元测试失败。我把一些虚拟的价值放入' _csrf'参数,它没有工作。

在发送请求之前我是否可以获得csrf令牌(单元测试时)?

3 个答案:

答案 0 :(得分:25)

您的回答uiroshan正在破坏csrf令牌的目的:使用您的配置它现在将是一个常量值(除非您的配置仅用于您的测试上下文,但您没有指定它。)

解决此问题的正确(且更简单)方法是:

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

...

@Test
public void testLogin() throws Exception {
    this.mockMvc.perform(post("/login")
            .param("username", "...")
            .param("password", "...")
            .with(csrf()))
        .andExpect(status().isFound())
        .andExpect(header().string("Location", "redirect-url-on-success-login"));
}

重要的部分是:.with(csrf()),它会将预期的_csrf参数添加到查询中。

csrf()静态方法由spring-security-test提供:

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-test</artifactId>
    <version>4.2.9.RELEASE / 5.1.1.RELEASE</version>
    <scope>test</scope>
</dependency>

您的单元测试需要以下导入才能访问它:

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

答案 1 :(得分:1)

通过创建自定义CsrfTokenRepository实现,我找到了解决此问题的方法。这将始终生成一个常量标记(例如&#34; test_csrf_token&#34;)。因此,我们可以将该令牌作为请求参数(因为它不会改变)与其他表单参数一起发送。以下是我为解决问题而采取的步骤。

  1. 创建一个实现CsrfTokenRepository接口的类。实现使用一些常量标记值生成令牌。

    public CsrfToken generateToken(HttpServletRequest request) {
       return new DefaultCsrfToken(headerName, parameterName, "test_csrf_token");
    }
    
    @Override
    public void saveToken(CsrfToken token, HttpServletRequest request, HttpServletResponse response) {
        if (token == null) {
            HttpSession session = request.getSession(false);
            if (session != null) {
                session.removeAttribute(sessionAttributeName);
            }
        } else {
            HttpSession session = request.getSession();
            session.setAttribute(sessionAttributeName, token);
        }
     }
    
     @Override
     public CsrfToken loadToken(HttpServletRequest request) {
        HttpSession session = request.getSession(false);
        if (session == null) {
           return null;
        }
        return (CsrfToken) session.getAttribute(sessionAttributeName);
     }
    
  2. 在安全配置中添加对csrf标记的引用。

    <http>
       <csrf token-repository-ref="customCsrfTokenRepository" />
       ....
    </http>
    
    <beans:bean id="customCsrfTokenRepository" class="com.portal.controller.security.TestCsrfTokenRepository"></beans:bean>
    
  3. 通过添加csrf请求参数来修改您的测试用例。

    request.addParameter("_csrf", "test_csrf_token");
    

答案 2 :(得分:0)

除了@Thierry答案之外,反应堆也有类似的解决方案。

使用WebTestClient致电后端时:

import org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.csrf

        // ...
        webTestClient.mutateWith(csrf()).post()...