温馨提示:本文翻译自stackoverflow.com,查看原文请点击:scala - Play Framework 2.6 CSRF and Session
playframework scala csrf

scala - Play Framework 2.6 CSRF和会话

发布于 2020-04-13 13:44:13

我有一个奇怪的问题。我正在网站上实现购物车功能,并使用会话存储购物车位置。我有一个POST操作来向购物车添加新职位,并且我启用了CSRF过滤器以保护网站安全。我在产品页面上用ajax来称呼它,所以第一次调用是可以的,但是第二次说是未授权的,在日志中有[CSRF] Check failed because no token found in headers for /cart但是它有。我称它为:

 $("form").submit(function(e){
        e.preventDefault();
        $.ajax({
            url: '/cart',
            method: 'POST',
            data: getCartPosition(),
            beforeSend: function(xhr){xhr.setRequestHeader('Csrf-Token', $('input[name=csrfToken]').val());},
            success: function (data, textStatus) {
                alert('Added!');
            },
            error: function (error) {
                alert('Error!');
            }
        });
    });

然后将CSRF令牌放在模板中的某个位置:

@CSRF.formField

它是在请求:

在此处输入图片说明

我已经在配置中启用了此功能

play.filters.csrf.bypassCorsTrustedOrigins=true 
play.filters.hosts {
  # Allow requests to example.com, its subdomains, and localhost:9000
  allowed = ["localhost:9000", "localhost:4200"]
}

但是奇怪的是,它似乎将csrfToken放入了会话中,因为在失败的请求之后,我会有这样的会话

Session(Map(cart -> {"positions":
[{"trackId":1},{"trackId":24},{"trackId":20}]}, 
username -> user, 
token -> 0639d0b0-e7c8-4e82-9aad-2a43044e72db, 
csrfToken -> e705413843ea96a6491a0e9e800ba36a712c4f70-1506542471068-0baeef7535eb9c889fb6fed2))

知道为什么会出现,我的add2cart动作看起来像:

private def cartAction(addToCartForm: Form[CartPosition], action: (Cart, CartPosition) => Cart)(implicit request: UserRequest[Any]) = {
    addToCartForm.fold(
      _ => BadRequest("Error!"),
      position => {
        getCart match {
          case Some(cart) => Ok("Ok").withSession("cart" -> Json.toJson(action(cart, position)).toString(), "username" -> request.session.get("username").getOrElse(""), "token" -> request.session.get("token").getOrElse(""))
          case _ => Ok("Ok, no").withSession("cart" -> Json.toJson(action(Cart(Seq.empty), position)).toString())
        }
      }
    )
  }

def addToCart() = guestAction { implicit request =>
    cartAction(addToCartForm.bindFromRequest, addCartPos)
  }

和addCartPos只是将位置添加到json

查看更多

提问者
cutoffurmind
被浏览
22
domino 2019-07-09 08:36

Play 2.7.3出现了相同的问题。

就我而言,该表单是由Twirl生成的,csrf token并且由于我使用的是ajax提交表单,因此我csrf token已从呈现的表单中复制了该表单,并将其传递给Play文档中所写的ajax标头。

该表单可以提交多次,因此我需要更新令牌。因此,我要通过传入csrf token控制器中的ajax响应play.filters.csrf.CSRF.getToken.get.value

但是不幸的是,第二次提交失败了,因为提到了苦味素

修复程序如Knut Arne Vedaa所述,将新令牌添加到会话中。我是用这种withSession方法做的。

因此,控制器响应如下所示:

 Ok(Json.obj(
    "status" -> (user != None),
    "notif" -> "Success login",
    "data" -> Map( 
       "adminUrl" -> "www.something ...", 
       "csrf" -> play.filters.csrf.CSRF.getToken.get.value
    )
 )).withSession(
    "uid" -> user.getOrElse(User()).id.toString,
    "csrfToken" -> play.filters.csrf.CSRF.getToken.get.value
 )

看起来没有问题,因为Play Framework没有在服务器上保留会话数据,因此在ajax请求之后必须在客户端站点中更新令牌是合乎逻辑的。主要问题是文档(在CSRF ajax部分中)未提及它,这很方便,因为人们根本没有按预期的顺序从A到Z读取文档。