[英]Play Framework 2.6 CSRF and Session
我遇到了奇怪的问题。 我正在我的网站上实现购物车功能,并使用会话来存储购物车位置。 我有一个 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
我在 Play 2.7.3 中遇到了同样的问题。
就我而言,该表单是由 Twirl 使用csrf token
生成的,并且因为我使用 ajax 提交表单,所以我从呈现的表单中复制了csrf token
并将其传递给 ajax 标头,如 Play 中所写文档。
表单可以多次提交,所以我需要更新令牌。 因此,我正在通过控制器中从play.filters.csrf.CSRF.getToken.get.value
获取的 ajax 响应新csrf token
。
但不幸的是,第二次提交失败了,正如cutoffurmind提到的那样。
修复方法如Knut Arne Vedaa所述,用于向会话添加新令牌。 我是通过withSession
方法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 阅读文档。
在我的情况下,解决方案是将play.filters.csrf.cookie.name
配置选项设置为 null 以外的值:
play.filters.csrf.cookie.name = csrf_token
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.