【发布时间】:2014-10-25 15:43:30
【问题描述】:
最近我们为使用 spring security 3.2 的项目引入了 CSRF 保护。
启用 CSRF 后,一些单元测试失败,因为请求中不存在 csrf 令牌。我在“_csrf”参数中放入了一些虚拟值,但它不起作用。
在发送请求之前(单元测试时)我是否可以获取 csrf 令牌?
【问题讨论】:
标签: spring-mvc junit spring-security csrf spring-mvc-test
最近我们为使用 spring security 3.2 的项目引入了 CSRF 保护。
启用 CSRF 后,一些单元测试失败,因为请求中不存在 csrf 令牌。我在“_csrf”参数中放入了一些虚拟值,但它不起作用。
在发送请求之前(单元测试时)我是否可以获取 csrf 令牌?
【问题讨论】:
标签: spring-mvc junit spring-security csrf spring-mvc-test
我找到了解决此问题的方法,方法是创建自定义 CsrfTokenRepository 实现。这将始终生成一个常量令牌(如“test_csrf_token”)。所以我们可以将该令牌作为请求参数(因为它不会改变)与其他表单参数一起发送。以下是我解决问题所遵循的步骤。
创建一个实现 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);
}
在您的安全配置中添加对 csrf 标记的引用。
<http>
<csrf token-repository-ref="customCsrfTokenRepository" />
....
</http>
<beans:bean id="customCsrfTokenRepository" class="com.portal.controller.security.TestCsrfTokenRepository"></beans:bean>
通过添加 csrf 请求参数来修改您的测试用例。
request.addParameter("_csrf", "test_csrf_token");
【讨论】:
解决这个问题的方法是:
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>5.3.5.RELEASE / 5.4.1</version>
<scope>test</scope>
</dependency>
您的单元测试将需要以下导入才能访问它:
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*;
【讨论】:
除了@Thierry 的回答之外,还有类似的反应堆栈解决方案。
使用WebTestClient 调用您的后端时:
import org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.csrf
// ...
webTestClient.mutateWith(csrf()).post()...
【讨论】: