2016-06-28 34 views
0

My Rails应用程序采用面向公众的形式,通过AJAX将用户输入作为字符串化的JSON传递给控制器​​。该表单专为脱机使用而设计,因此每次访问除第一页以外的表单页面都是通过浏览器缓存(使用缓存清单)提供的。我有一个问题,表单提交返回一个422 unprocessable entity错误,除非在导航到表单页面之前浏览器历史记录已被清除......也就是说用户只能进行一个表单提交,所有后续提交内容为422,除非他们清除历史并返回到表单来刷新缓存。不幸的是,这不会飞。Rails/AJAX缓存表单提交 - 422无法处理的实体

我对Rails的安全性并没有太大的经验,但我的印象是这与CSRF保护有关,而且对于除第一个表单页之外的任何访问,一个陈旧的CSRF标记正在通过。

我的AJAX请求看起来就像这样:

$.ajax({ 
    url: "post/submission", 
    type: "POST", 
    dataType: "json", 
    beforeSend: function(xhr) {xhr.setRequestHeader("X-CSRF-Token", $("meta[name='csrf-token']").attr("content"))}, 
    data: {"post" : postParameter}, 
    success: function(response){ 
     window.location = '/post/approval'; 
    } 
}); 

目前,布局页包含<%= csrf_meta_tags %>,我必须在应用程序控制器标准protect_from_forgery with: :exception

需要注意这种形式的最终结构件是,虽然表单本身是面向公众的,它需要用户登录后,点击提交按钮 - 所以提交将没有有效的登录成功。

有没有一种安全的方法可以解决这个问题?我相信这一点不言而喻,但我不能让我的用户在每次提交后清除历史记录并重新缓存表单。

+0

如果您想要保持CSRF保护,则必须在呈现表单时重新生成令牌。 '<%= hidden_​​field_tag:authenticity_token,form_authenticity_token%>'这样做。从缓存中显示页面时的ajax调用会执行。查看更多[here](http://stackoverflow.com/questions/8503447/rails-how-to-add-csrf-protection-to-forms-created-in-javascript)和[here](http:// stackoverflow .COM /问题/ 829046 /怎么办 - 我 - 检测 - 如果-A-用户已经-了到一个页面,使用最后退按钮)。 –

+0

嗯......我应该提到的另一件事是,表单不是以标准的Rails方式与表单助手等构建的。事实上,表单并非HTML表单中的一种表单 - 在提交时,用户输入被编译成一个JSON字符串,然后通过AJAX将其解析出来并插入到数据库中...所以这里没有一个地方可以放置一个'hidden_​​field_tag'。我认为在你加入的第二个链接中可能有一些很好的解决方案 - 我会测试它们,并让你知道我是否可以得到任何东西。感谢您的时间。 – skwidbreth

+0

当然,您只需将'form_authenticity_token'的结果传递给您的前端,而不管它是作为模板呈现的一部分完成还是通过ajax调用传输。从那里你可以将它包含到你的POST负载中。 –

回答

1

为了在提交来自JavaScript的POST请求时保持CSRF保护,您需要在表单的有效内容或请求标头中提供当前身份验证令牌。

当页面从浏览器缓存中加载时,其身份验证令牌可能已过时,因此需要从后端刷新页面。一种方法是检测"page loaded from browser cache"事件并运行一个ajax请求来获取新的令牌,后者应该由form_authenticity_token方法生成。

接收到的认证令牌可以用于后续的JavaScript POST请求。例如,描述了检测从缓存中加载的页面的技术,例如, in this StackOverflow answer

相关问题