使用 AJAX 向 Rails 发送 Authenticity Token 的正确方法

Posted

技术标签:

【中文标题】使用 AJAX 向 Rails 发送 Authenticity Token 的正确方法【英文标题】:Proper way to send an Authenticity Token with AJAX to Rails 【发布时间】:2011-11-25 12:37:57 【问题描述】:

这可行,但由于缺少真实性令牌而停止:

$(".ajax-referral").click(function()
  $.ajax(type: "POST", url: $(this).parent("form").attr("action"), dataType: "script");
  return false;
);

所以我尝试像这样添加它:

$(".ajax-referral").click(function()
  $.ajax(type: "POST", url: $(this).parent("form").attr("action") + "?&authenticity_token=" + AUTH_TOKEN, dataType: "script");
  return false;
);

它正确地将 auth_token 作为参数传递,但似乎丢失了我的表单的其余部分。

是否要同时发送有效的表单数据和真实性令牌?

这是一个 Rails 环境。我脑子里有这个。

= javascript_tag "var AUTH_TOKEN = '#form_authenticity_token';" if protect_against_forgery?

我尝试过的事情

1.

= hidden_field :authenticity_token, :value => form_authenticity_token

2.

$.ajax(type: "POST", url: $(this).parent("form").attr("action"), dataType: "script", authenticity_token: AUTH_TOKEN);

3.

// Always send the authenticity_token with ajax
$(document).ajaxSend(function(event, request, settings) 
    if ( settings.type != 'GET' ) 
        settings.data = (settings.data ? settings.data + "&" : "")
            + "authenticity_token=" + encodeURIComponent( AUTH_TOKEN );
    
);

【问题讨论】:

【参考方案1】:

在 Rails 5.1+ 中,如果您对 AJAX 请求使用内置的 Rails JS 助手(来自 rails-ujs),则会自动附加 CSRF 令牌,例如:

Rails.ajax(
  url: "/your/url",
  type: "POST",
  data: "a=1&b=2",
  success: function(data) 
    console.log(data);
  
);

此库还为您提供了手动获取 CSRF 令牌的助手,如果您需要它:

Rails.csrfToken();

【讨论】:

【参考方案2】:

只需使用 form_tag 自动包含 CSRF 令牌参数。 Rails 支持“Unobtrusive Javascript”,这意味着表单仍将通过 AJAX 提交。控制器动作支持“respond_to”块,您可以使用 .js.erb 扩展名在网页上进行更改以响应表单提交。

【讨论】:

【参考方案3】:

在我通过 JS 在请求标头上设置 X-CSRF-Token 值之前,这些都不适合我,如下所示:

request.setRequestHeader('X-CSRF-Token', token)

token 当然是 CSRF 令牌。我是从<meta name="csrf-token"> 标签得到的,没有使用encodeURIComponent()

更新,因为事实证明这对某些人有用

总而言之:

var token = document.querySelector('meta[name="csrf-token"]').content
request.setRequestHeader('X-CSRF-Token', token)

【讨论】:

这是 Rails 5+ 的解决方案,其中 per_form_csrf_tokens must be set 用于单页应用程序情况。【参考方案4】:

如果您在顶部有以下 ERB,则默认情况下,此标记也已出现在 application.html.erb 布局文件头部的“元”标签之一中:

<%= csrf_meta_tag %>

那个ERB大致呈现给:

<meta content="abc123blahblahauthenticitytoken" name="csrf-token">

然后您可以使用 jQuery 使用以下代码获取它:

var AUTH_TOKEN = $('meta[name=csrf-token]').attr('content');

【讨论】:

在 Rails 5 中可能无法实现,因为每个表单都有自己的身份验证令牌 blog.bigbinary.com/2016/01/11/… 好点,@FabrizioBertoglio 我认为这条评论是围绕 Rails 3/Rails 4 天写的。在以后的版本中可能无法做到这一点。【参考方案5】:

我刚刚遇到了这个问题,但我在 application.js 文件中尝试了这种方法:

$(document).ajaxSend(function(e, xhr, options) 
  if (options.data == null) 
    options.data = ;
  
  options.data['authenticity_token'] = token;
);

这是我得到这个想法的原始问题:ajaxSend Question

【讨论】:

【参考方案6】:

谢谢!

只是为了澄清更常见的用途。

您需要在您的脑海中使用带有 var AUTH_TOKEN 的 js 标记。应该是这样的。

<%= csrf_meta_tag %>
<%= javascript_tag "var AUTH_TOKEN = '#form_authenticity_token';" if protect_against_forgery? %>

然后,如果您不需要使用 parent(form) 或类似的东西,只需将您的authentity_token=AUTH_TOKEN 放入 ajax 数据中。

$.ajax(
  type: 'post',
  dataType:'text',
  data: "user_id="+user_id+"&authenticity_token="+AUTH_TOKEN,
  url:'/follow/unfollow'
)

感谢上面的人分享这些知识!

【讨论】:

授权令牌是 base64 编码的。它可能包含一个加号 (+)。如果您没有正确转义 auth 令牌,rails 会将其解析为空格,因为 url 编码。【参考方案7】:

实际上,您正在读取表单的action 属性并向其发送post ajax 请求。要发送表单数据,您必须提交表单,或者您可以序列化表单数据并在 ajax 请求中发送,例如

$(".ajax-referral").click(function()
  $.ajax(
      type: "POST", 
      url: $(this).parent("form").attr("action") + "?&authenticity_token=" + AUTH_TOKEN, 
      data:$(this).parent("form").serialize(),
      dataType: "script"
      );
  return false;
);

这样做将序列化您的表单数据并将其与 ajax 请求一起发送,并且真实性令牌已通过查询字符串发送

【讨论】:

我不得不使用 encodeURIComponent( AUTH_TOKEN ),但是一旦我这样做了,在查询字符串中使用它就可以了。 token 不应在 url 中发送! @Gacci 除非您的端点 URL 是 http(即纯文本),否则参数(在这种情况下为 authenticity_token)将被加密。所以我宁愿说“令牌应该总是被发送到 httpS URL”。无论如何,Ajax 调用的 URL 都不会保存在浏览器历史记录中。【参考方案8】:

您可以在表单本身中包含 AUTH_TOKEN,作为隐藏输入。

<input type="hidden" name="AUTH_TOKEN">1234abcd</input>

【讨论】:

&lt;input type="hidden" name="authenticity_token" value=$('meta[name=csrf-token]').attr('content')/&gt;(请注意,我使用 JSX 进行模板化。将 替换为适合您的模板语言的任何内容。

以上是关于使用 AJAX 向 Rails 发送 Authenticity Token 的正确方法的主要内容,如果未能解决你的问题,请参考以下文章

通过 ajax rails 2 向 rails 3 提交表单

向大量收件人发送电子邮件的最佳实践 (Rails + SendGrid)

我的 jquery AJAX POST 请求无需发送真实性令牌(Rails)即可工作

Rails ajax 表单发送电子邮件但不在“成功”下运行代码

RoR:发送附加数据以使用 Jquery AJAX 提交

使用方法对 link_to 的 ajax 请求:在禁用 javascript 的情况下放入 rails