如何在表单的 AJAX 发布请求中传递 CSRF 令牌?

Posted

技术标签:

【中文标题】如何在表单的 AJAX 发布请求中传递 CSRF 令牌?【英文标题】:How to pass along CSRF token in an AJAX post request for a form? 【发布时间】:2018-01-10 06:24:58 【问题描述】:

我正在使用 Scala Play! 2.6 框架,但这可能不是问题。我正在使用他们的 javascript 路由 - 它似乎工作正常,但它有问题。我有一个表单,它在渲染时会产生这个,带有一个 CSRF 令牌:

<form method="post" id="myForm" action="someURL">

<input name="csrfToken" value="5965f0d244b7d32b334eff840...etc" type="hidden">
  <input type="text" id="sometext">
  <button type="submit"> Submit! </button>

</form>

这大概是我的 AJAX:

$(document).on('submit', '#myForm', function (event) 

 event.preventDefault();
   var data = 
    textvalue: $('#sometext').val()
   
 var route = jsRoutes.controllers.DashboardController.postNewProject()
 $.ajax(
    url: route.url,
    type: route.type,
    data : JSON.stringify(data),
    contentType : 'application/json',
    success: function (data)  ...      ,
    error: function (data)  ...  
        )

);

但是当我发布这个时,我从我的服务器收到一个未经授权的响应,并且我在 IntelliJ 中的控制台告诉我 CSRF 检查失败。我将如何在请求中传递 CSRF 令牌?

【问题讨论】:

你解决了这个问题吗? 不是真的,但现在我关闭了 CSRF 检查...不要这样做 也许我的这个问题可能会有所帮助或提供帮助-> ***.com/questions/45017920/… 谢谢 - 我一直有这个问题 - 我在上面发布的表格遵循类似的方法添加 @CSRF.formField (我刚刚在渲染后显示它,因为它被发送给客户端)。我现在的问题是,当我通过 AJAX 请求发布时,它没有发送或检查它。 @KGCybeX 我已经设法修复它,如果您尝试使用 AJAX 发布任何内容,您稍后可能会遇到此问题 - 请参阅下面的答案。 【参考方案1】:

好的,经过几个小时的斗争并尝试在 subject 上解密 Play 经常缺少的上下文文档,我明白了。

因此,从他们的文档中:

为了对非浏览器请求进行简单保护,Play 仅检查 标头中带有 cookie 的请求。如果您提出请求 AJAX,可以在html页面中放置CSRF token,然后添加 使用Csrf-Token 标头发送请求。

然后没有代码或示例。谢谢玩。非常具有描述性。不管怎样,方法如下:

在您的 view.html.formTemplate 中,您可以使用 IntelliJ 编写:

@()
<form method="post" id="myForm" action="someURL">

@helper.CSRF.formField  <!-- This auto-generates a token for you -->
  <input type="text" id="sometext">
  <button type="submit"> Submit! </button>

</form>

这会在交付给客户端时呈现如下:

<form method="post" id="myForm" action="someURL">

<input name="csrfToken" value="5965f0d244b7d32b334eff840...etc" type="hidden">
  <input type="text" id="sometext">
  <button type="submit"> Submit! </button>

</form>

好的,差不多了,现在我们必须创建我们的 AJAX 调用。我的所有内容都在一个单独的 main.js 文件中,但如果需要,您也可以将其放入您的 view.html.formTemplate

$(document).on('submit', '#myForm', function (event) 

 event.preventDefault();
   var data = 
    myTextToPass: $('#sometext').val()
   
 // LOOK AT ME! BETWEEN HERE AND
 var token =  $('input[name="csrfToken"]').attr('value')
    $.ajaxSetup(
        beforeSend: function(xhr) 
            xhr.setRequestHeader('Csrf-Token', token);
        
    );
// HERE
 var route = jsRoutes.controllers.DashboardController.postNewProject()
 $.ajax(
    url: route.url,
    type: route.type,
    data : JSON.stringify(data),
    contentType : 'application/json',
    success: function (data)  ...      ,
    error: function (data)  ...  
        )

);

使用这一行: var token = $('input[name="csrfToken"]').attr('value') 您正在提取表单字段中自动生成的 CSRF 令牌,并在 var 中获取其值以在 Javascript 中使用。

所有 AJAX 的另一个重要部分在这里:

$.ajaxSetup(
            beforeSend: function(xhr) 
                xhr.setRequestHeader('Csrf-Token', token);
            
        );

使用$.ajaxSetup,您可以设置标题中的内容。这是你必须从他们的文档中推断出来的:

使用 Csrf-Token 标头将其添加到请求中。

祝你好运!让我知道这是否清楚。


注意:使用lusca时,请使用X-CSRF-Token而不是Csrf-Token

【讨论】:

谢谢!我几乎是这样写的,因为我知道有一天我需要自己再次参考它。 您的回答对他人有很大帮助! :) @NateH06 如果你不使用“
”和“@helper.CSRF.formField”,而是使用"@CSRF(route.Something.something(something))" 其中 "something" 是一个 JavaScript 变量,应该被解析成 "@CSRF(route.Something.something(something))"?这甚至可能吗?
如果您尝试使用 JS 滚动您自己的令牌,我将完全避免使用他们的 @CSRF 助手调用,我不确定它是否需要参数。只需自己生成 HTML 并插入您认为合适的值。 &lt;input name="yourCsrfToken" value="route.Something(something) etc" type="hidden"&gt; 然后把上面的var设置改成:var token = $('input[name="yourCsrfToken"]').attr('value') 完美运行!我必须添加“X-CSRF-Token”,因为我使用的是 lusca【参考方案2】:

来自 JSP

<form method="post" id="myForm" action="someURL">
    <input name="csrfToken" value="5965f0d244b7d32b334eff840...etc" type="hidden">    
</form>

这是在挣扎了 3 小时后对我有用的最简单的方法,只需像这样从输入隐藏字段中获取令牌,并且在执行 AJAX 请求时只需在标头中传递此令牌,如下所示:-

来自 JQuery

var token =  $('input[name="csrfToken"]').attr('value'); 

来自纯 Javascript

var token = document.getElementsByName("csrfToken").value;

最终的 AJAX 请求

$.ajax(
          url: route.url,
          data : JSON.stringify(data),
          method : 'POST',
          headers: 
                        'X-CSRFToken': token 
                   ,
          success: function (data)  ...      ,
          error: function (data)  ...  
);

现在您不需要在 web config 中禁用 crsf 安全性,这也不会在控制台上出现 405(Method Not Allowed) 错误。

希望这会对人们有所帮助..!!

【讨论】:

这确实有帮助,但前提是使用“X-CSRFToken”(没有第二个破折号)!【参考方案3】:

使用 Csrf-Token 标头将其添加到请求中。

感谢 NateH06 提供标题名称!我试图通过 ajax 函数调用为“删除”按钮发送 csrf 令牌,但我遇到了以下问题:

@import helper._
....
<button id="deleteBookBtn" class="btn btn-danger"
        data-csrf-name="@helper.CSRF.getToken.name"
        data-csrf-value="@helper.CSRF.getToken.value"
        data-delete-url="@routes.BooksController.destroy(book.id)"
        data-redirect-url="@routes.HomeController.index()">Delete</button>

我无法在 onclick() 事件中添加在线 js,因为 Play 2.6 中也设置了 CSP。

拒绝执行内联事件处理程序,因为它违反了以下内容安全策略指令:“default-src 'self'”。

在 JS 文件上:

function sendDeleteRequest(event) 
  url = event.target.getAttribute("data-delete-url")
  redirect = event.target.getAttribute("data-redirect-url")
  csrfTokenName = event.target.getAttribute("data-csrf-name")
  csrfTokenValue = event.target.getAttribute("data-csrf-value")
  $.ajax(
    url: url,
    method: "DELETE",
    beforeSend: function(request) 
      //'Csrf-Token' is the expected header name, not $csrfTokenName
      request.setRequestHeader(/*$csrfTokenName*/'Csrf-Token', csrfTokenValue);
    ,
    success: function() 
      window.location = redirect;
    ,
    error: function() 
      window.location.reload();
    
  )


var deleteBookBtn = document.getElementById("deleteBookBtn");
if(deleteBookBtn) 
    deleteBookBtn.addEventListener("click", sendDeleteRequest);

将标头名称设置为'Csrf-Token' 后,ajax 调用完美!

【讨论】:

【参考方案4】:

如果它对正在谷歌搜索试图理解为什么他们无法获得正确令牌出现的其他人有用....我一直在为 Play 后端/React 前端组合的相同问题而苦苦挣扎- 因此无法(轻松)使用 token-in-htmlpage 技术我最终遇到了另一种在 cookie 中设置当前令牌的解决方案......只需添加:

play.filters.csrf 
  cookie.name = "csrftoken"

到 application.conf 和 csrftoken cookie 将被设置为令牌。然后,我使用https://www.npmjs.com/package/js-cookie 来获取我的 JS 代码中的值并将其发送回请求标头中 - 不包括此处的代码,因为它是 React 而不是 jQuery 对于 OP 并且不想混淆问题。

【讨论】:

【参考方案5】:

您可以使用headers 选项添加Csrf-Token 标头。

$.ajax(
    url: '@routes.MyController.myPostAction()',
    method: 'post',
    headers: 
        'Csrf-Token': '@play.filters.csrf.CSRF.getToken.map(_.value)'
    ,
    data: 
        name: '@name'
    ,
    success: function (data, textStatus, jqXHR) 
        location.reload();
    ,
    error: function (jqXHR, textStatus, errorThrown) 
        debugger;
    
);

【讨论】:

【参考方案6】:

如Play Framework 2.6 Documentation 中所述,您可以使用 Play 生成的令牌设置一个 'Csrf-Token' Header:

如果您使用 AJAX 发出请求,您可以将 CSRF 令牌放在 HTML 页面中,然后使用 Csrf-Token 标头将其添加到请求中。

在 Scala 模板中,您可以使用 @helper.CSRF.getToken.value 获取令牌值

在jQuerys Documentation 之后,您可以通过使用ajaxSetup 配置jQuery 为所有Ajax 请求设置一次

$.ajaxSetup(
  beforeSend: function(xhr) 
    xhr.setRequestHeader('Csrf-Token', '@helper.CSRF.getToken.value');
  
);

或者通过像这样配置headers 对象来设置每个请求的标头:

$.ajax(
  url: route.url,
  ...
  headers: 
    'Csrf-Token': '@helper.CSRF.getToken.value'
  
);

【讨论】:

【参考方案7】:

slim csrf gurd,在 jQuery 中传递 csrf key 和 token 的方式,用于带有 twig 的 slim 应用程序

$(document).ready(function()
    $.ajaxPrefilter(function(options, originalOptions, jqXHR)

        var allowedMethod = ["post", "put", "delete"];

        if (allowedMethod.includes(options.type.toLowerCase())  ) 
            // initialize `data` to empty string if it does not exist
            options.data = options.data || "";

            // add leading ampersand if `data` is non-empty
            options.data += options.data?"&":"";

            // add _token entry
            options.data += "csrf.keys.name=csrf.name&csrf.keys.value=csrf.value";
        
    );
);

【讨论】:

以上是关于如何在表单的 AJAX 发布请求中传递 CSRF 令牌?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 AJAX 请求 Laravel + Vue.js 传递 CSRF 令牌

django:csrf_token 用于单个页面上的多个表单和 ajax 请求

Ajax GET 请求后表单提交失败(无效的 CSRF 令牌)。怎么修?

什么是CSRF跨站请求伪造?(from表单效验csrf-ajdax效验csrf-Ajax设置csrf-CBV装饰器验证csrf)

带有 CSRF 的 Symfony2 表单通过 JQuery AJAX 传递

更改提交时 Ajax 表单上的 CSRF 令牌不匹配