我在测试使用Play的CSRF保护的控制器时遇到了一些问题。为了证明这一点,我创建了一个非常简单的Play应用程序,可以最小化地解决问题。
https://github.com/adamnfish/csrftest
完整的详细信息在该存储库的自述文件中,但总结一下:
考虑一个旨在处理表单提交的控制器。它有一个使用CSRFAddToken的GET方法和一个使用CSRFCheck的POST方法。前者在请求中添加CSRF令牌,以便可以在包含有效令牌的呈现视图中放置表单域。提交该表单时,如果CSRF检查通过且提交有效,则会发生其他情况(通常是重定向)。如果表单提交无效,表单提交将与任何错误一起重新显示,以便用户更正表单并再次提交。
这很棒!
但是,在测试中我们现在遇到了一些问题。要测试控制器,您可以在测试中向其传递虚假请求。可以通过将nocheck标头添加到伪请求来跳过CSRF检查本身,但无法呈现视图,因为没有可用于生成表单字段的标记。测试失败,出现RuntimeException,“缺少CSRF令牌(csrf.scala:51)”。
鉴于它在实际运行时有效,但在测试中没有,看起来这对于FakeRequests在Play测试中的运行方式一定存在问题,但我可能做错了。我按http://www.playframework.com/documentation/2.2.1/ScalaCsrf所述实施了CSRF保护,并按http://www.playframework.com/documentation/2.2.1/ScalaFunctionalTest所述进行了测试。如果有人设法测试CSRF保护表单,我会很感激。
答案 0 :(得分:4)
一种解决方案是使用浏览器进行测试,例如Fluentlenium,因为这将管理cookie等,因此CSRF保护应该都能正常工作。
另一种解决方案是向FakeRequest添加一个会话,使其包含一个令牌,例如:
FakeRequest().withSession("csrfToken" -> CSRF.SignedTokenProvider.generateToken)
显然,如果您经常这样做,您可以创建一个帮助方法来为您完成。
答案 1 :(得分:4)
对那些对Java感兴趣的人的答案:我通过添加
在Java版本的Play Framework 2.2中使用它.withSession(CSRF.TokenName(), CSRFFilter.apply$default$5().generateToken())
到fakeRequest()
答案 2 :(得分:1)
从@plade开始,我在我的基础测试类中添加了一个辅助方法:
protected static FakeRequest csrfRequest(String method, String url) {
String token = CSRFFilter.apply$default$5().generateToken();
return fakeRequest(method, url + "?csrfToken=" + token)
.withSession(CSRF.TokenName(), token);
}
答案 3 :(得分:1)
对那些仍然感兴趣的人:我设法通过在测试中启用CSRF保护来全局解决这个问题。然后,应用程序将为每个不包含一个请求创建一个令牌。请参阅我对this question
的回答答案 4 :(得分:1)
对于那些可能感兴趣的人,我为play 2.5.x创建了一个特性: https://stackoverflow.com/a/40259536/3894835
然后,您可以在测试请求中使用它,例如控制器的addToken {}:
val fakeRequest = addToken(FakeRequest(/* params */))
答案 5 :(得分:1)
我在基本集成测试类中使用以下方法:
def csrfRequest(method: String, uri: String)(implicit app: Application): FakeRequest[AnyContentAsEmpty.type] = {
val tokenProvider: TokenProvider = app.injector.instanceOf[TokenProvider]
val csrfTags = Map(Token.NameRequestTag -> "csrfToken", Token.RequestTag -> tokenProvider.generateToken)
FakeRequest(method, uri, FakeHeaders(), AnyContentAsEmpty, tags = csrfTags)
}
然后,您可以在使用FakeRequest
的测试中使用它。