Play Framework 2.6 CSRF和会话 [英] Play Framework 2.6 CSRF and Session

查看:86
本文介绍了Play Framework 2.6 CSRF和会话的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I got strange issue. I'm implementing cart functionality on my website and I use session to store cart positions. I have a POST action to add new position to cart, and I have CSRF filter enabled to secure website. I call it with ajax on a product page, so first call is okay, but second says Unauthorized and in logs there are [CSRF] Check failed because no token found in headers for /cart. But it has. I call it with:

 $("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令牌放在模板中的某个位置:

and I put CSRF token in template somewhere:

@CSRF.formField

它正在请求中:

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

I have enabled this in config

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

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

But what is strange that it seems it puts csrfToken in session, because after failed request I have session like this

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

知道为什么会出现,我的add2cart动作如下:

Idk why it's there, my add2cart action looks like:

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

and addCartPos just adds position to json

推荐答案

我在Play 2.7.3中遇到了同样的问题.

I've got same issue with Play 2.7.3.

在我的情况下,表单是由Twirl用csrf token生成的,并且由于我使用ajax提交表单,因此我已经从呈现的表单中复制了csrf token并将其传递给ajax标头如Play的文档中所述.

In my case, the form is generated by the Twirl with the csrf token and because I'm using ajax to submit the form, I've copied the csrf token from the rendered form and pass it to the ajax header as writen in the Play's documentation.

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

The form can be submitted multiple times so I need to updated the token. Therefore I'm passing through ajax response new csrf token taken in the controller from play.filters.csrf.CSRF.getToken.get.value.

但是不幸的是,由于 cutoffurmind 所述,第二次提交失败了.

But unfortunatelly, the second submission failed as cutoffurmind mentioned.

此修复程序如 Knut Arne Vedaa 所述,将新令牌添加到会话中.我是通过withSession方法做到的.

And the fix is as described by Knut Arne Vedaa to add new token to the session. I did it by the withSession method.

所以控制器的响应看起来像这样:

So the controller response is looking like this:

 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读取文档.

It doesn't look like an issue as Play Framework doesn't have session data kept on the server, so it is logical that the token has to be updated in the client site after the ajax request. The main issue is that it is not mentioned in the documentation (in the CSRF ajax section), what could be handy as people simply doesn't read the doc from A to Z in expected order.

这篇关于Play Framework 2.6 CSRF和会话的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆