【问题标题】:Testing scala Play (2.2.1) controllers with CSRF protection使用 CSRF 保护测试 scala Play (2.2.1) 控制器
【发布时间】:2013-11-19 05:46:18
【问题描述】:

我在测试使用 Play 的 CSRF 保护的控制器时遇到了一些问题。为了证明这一点,我创建了一个非常简单的 Play 应用程序,该应用程序几乎不会出现问题。

https://github.com/adamnfish/csrftest

完整的详细信息在该存储库的 README 中,但在这里总结一下:

考虑一个设计用于处理表单提交的控制器。它有一个使用 CSRFAddToken 的 GET 方法和一个使用 CSRFCheck 的 POST 方法。前者将 CSRF Token 添加到请求中,以便可以将包含有效令牌的表单字段放入呈现的视图中。当提交该表单时,如果 CSRF 检查通过并且提交有效,则会发生其他事情(通常是重定向)。如果表单提交无效,则会重新显示表单提交以及任何错误,以便用户可以更正表单并再次提交。

这很好用!

但是,在测试中我们现在遇到了一些问题。要测试控制器,您可以在测试中向它传递一个虚假请求。可以通过将 nocheck 标头添加到假请求来跳过 CSRF 检查本身,但由于没有可用于生成表单字段的令牌,因此无法呈现视图。测试失败,出现 RuntimeException,“Missing CSRF Token (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 保护的表单,我将不胜感激。

【问题讨论】:

    标签: scala testing playframework-2.0 csrf


    【解决方案1】:

    一种解决方案是使用浏览器进行测试,例如 Fluentlenium,因为这将管理 cookie 等,因此 CSRF 保护应该都能正常工作。

    另一种解决方案是在 FakeRequest 中添加一个会话,使其包含一个令牌,例如:

    FakeRequest().withSession("csrfToken" -> CSRF.SignedTokenProvider.generateToken)
    

    显然,如果你经常这样做,你可以创建一个帮助方法来为你做这件事。

    【讨论】:

    • 我正在使用模拟和依赖注入直接测试控制器,因此这里不能选择硒风格的集成测试。向会话添加一个有效的、生成的令牌确实会阻止错误的发生。非常感谢!我仍然觉得这揭示了 Play 运行测试的方式中的一个错误,但我可以将它放在邮件列表中。再次感谢。
    • 有没有办法在 Java 中做到这一点?
    【解决方案2】:

    对于那些对 Java 感兴趣的人的奖励答案:我通过添加使其在 Play Framework 2.2 的 Java 版本中工作

    .withSession(CSRF.TokenName(), CSRFFilter.apply$default$5().generateToken())
    

    fakeRequest()

    【讨论】:

    • 感谢分享。我不喜欢 Java 版本的 Play 进行测试,所以我会相信你的话 :-)
    【解决方案3】:

    从@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);
    }
    

    【讨论】:

    • 玩 2.5.X 你需要使用CSRFFilter.apply$default$3().generateToken();CSRFFilter.apply$default$1().tokenName()
    【解决方案4】:

    致那些仍然感兴趣的人:我设法通过在测试中启用 CSRF 保护来解决这个问题。然后,应用程序将为每个不包含令牌的请求创建一个令牌。查看我对this question的回复

    【讨论】:

      【解决方案5】:

      对于那些可能感兴趣的人,我为 play 2.5.x 创建了一个 trait: https://stackoverflow.com/a/40259536/3894835

      然后您可以在您的测试请求中使用它,例如控制器的 addToken{}:

      val fakeRequest = addToken(FakeRequest(/* params */))
      

      【讨论】:

        【解决方案6】:

        我在我的基础集成测试类中使用以下方法:

        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的测试中使用它。

        【讨论】:

          猜你喜欢
          • 2016-02-21
          • 2014-10-25
          • 2015-01-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-09-09
          相关资源
          最近更新 更多