【问题标题】:Rails and ajax request: not using csrf working?Rails 和 ajax 请求:不使用 csrf 工作?
【发布时间】:2012-05-10 04:54:54
【问题描述】:

我正在使用此代码向 rails 发出 AJAX POST 请求:

  var new_note = {
    title: "New note"
  };
  $.post('/notes.json',
    {
      auth_token: auth_token,
      note: new_note
    },
    function(data, textStatus, jqXHR){
      console.log(textStatus);
      console.log(jqXHR);
      var createdNoteIndex = self.notes.push(new Note());
      self.openNote(self.notes()[createdNoteIndex - 1]);
    }, "json")
    .error(function(jqXHR, textStatus, errorThrown){
      alert("error");
      console.log(jqXHR);
      console.log(textStatus);
      console.log(errorThrown);
    });

我忘了插入 csrf 令牌,所以我认为创建操作会失败:

  # POST /notes.json
  def create
    @note = current_user.notes.new(params[:note])

      if @note.save
        respond_with { render json: @note, status: :created, location: @note }
      else
        respond_with { render json: @note.errors, status: :unprocessable_entity }
      end
  end

但是数据库中的记录已经创建,而请求以 500 错误结束:

Started POST "/notes.json" for 127.0.0.1 at 2012-04-30 15:26:33 +0200
Processing by NotesController#create as JSON
  Parameters: {"auth_token"=>"zJzKxPnvx5dQDTcFWi5k", "note"=>{"title"=>"New note"}}
MONGODB (0ms) taccuino_development['users'].find({:_id=>BSON::ObjectId('4f9c670a809ad20869000002')}).limit(-1).sort([[:_id, :asc]])
MONGODB (0ms) taccuino_development['notes'].insert([{"_id"=>BSON::ObjectId('4f9e9309809ad223f5000007'), "title"=>"New note", "user_id"=>BSON::ObjectId('4f9c670a809ad20869000002')}])
Completed 500 Internal Server Error in 8ms

AbstractController::DoubleRenderError (Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like "redirect_to(...) and return".):
  app/controllers/notes_controller.rb:26:in `create'


  Rendered /home/matteo/.rvm/gems/ruby-1.9.3-p194/gems/actionpack-3.2.3/lib/action_dispatch/middleware/templates/rescues/_trace.erb (4.2ms)
  Rendered /home/matteo/.rvm/gems/ruby-1.9.3-p194/gems/actionpack-3.2.3/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb (1.5ms)
  Rendered /home/matteo/.rvm/gems/ruby-1.9.3-p194/gems/actionpack-3.2.3/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb within rescues/layout (14.8ms)

我没有禁用 csrf 保护,所以它应该给出关于令牌丢失的错误,但它没有......

编辑:

在阅读了我的两个答案后:

  • 删除了 jquery_ui 文件

添加此代码以替换 csrf 令牌的 jquery_ui 函数,并设置 auth_token 为设计:

  $.ajaxSetup({
    beforeSend: function(xhr, settings) {
      if (settings.crossDomain) return;              
      var csrf_token = $('meta[name="csrf-token"]').attr('content');
      var auth_token = $('meta[name="auth_token"]').attr('content');

      xhr.setRequestHeader('X-CSRF-Token', csrf_token);
      xhr.setRequestHeader('auth_token', auth_token);
    }
  });

删除了 before_file authenticate_user!来自控制器,并将与 current_user 相关的创建操作替换为不同的操作:

  def create
    @note = Note.new(params[:note])

      if @note.save
        respond_with { render json: @note, status: :created }
      else
        respond_with { render json: @note.errors, status: :unprocessable_entity }
      end
  end

然后我禁用了 CSRF 保护,但我仍然遇到同样的错误......所以问题是另一个问题,但我真的不明白什么会导致双重重定向,因为记录是在数据库中正确创建的...

【问题讨论】:

  • 我认为这个错误根本与 csrf 无关。哪一行是第 26 行(回溯提到的那一行)
  • 是的,我刚刚发现它使用了 respond_with 的语法,我在这里使用了与 format.json 相同的语法,但似乎不一样......抱歉为你失去的时间

标签: ruby-on-rails ajax post csrf csrf-protection


【解决方案1】:

如果您已将jquery_ujs.js 包含在您的应用程序中,CSRF 令牌将自动添加到 AJAX 请求中。你可以看到here

这也与您的DoubleRenderError btw 无关。那是你错误地使用了respond_with

编辑:

不要禁用 CSRF 保护。只是不要。

你不需要自己添加令牌,一切都会自动进行。

您的操作导致错误的原因是因为respond_with。如果您只响应 json 请求,它应该是这样的:

# POST /notes.json
def create
  @note = current_user.notes.new(params[:note])

  if @note.save
    render json: @note, status: :created, location: @note
  else
    render json: @note.errors, status: :unprocessable_entity
  end
end

但是,因为 Rails 已经知道这种模式(这是一种约定),所以您可以将其缩短为:

respond_to :json #, :html, :xml (optionally)

def create
  @note = current_user.notes.create(params[:note])
  respond_with @note
end

有关respond_with 的更多详细信息是here

【讨论】:

  • 好的,但是:1-)我读到 Rails 将 http 标头显示为参数,但我在请求的参数中看不到 X-CSRF-Token 参数
  • 这是标题,而不是参数。为此使用浏览器的开发工具:Rails 日志默认不包含标题。
  • 您可以通过request.headers['X-CSRF-Token']获取CSRF令牌。标头不是参数的一部分。
【解决方案2】:

大约一年前,由丢失或无效的 csrf 令牌触发的行为发生了变化。新行为不是引发异常,而是重置会话,因此处理请求时就像用户未登录一样。

您可以通过定义handle_unverified_request 并在那里实现所需的行为来控制此行为。

【讨论】:

  • 这似乎是我的情况,但正如@iain 在他的回答中指出的那样,因为我正在使用(嗯,只插入它)jquery_ujs.js 它应该自动包含 teh 令牌......虽然它没有t raise 和 exception 它不应该保存记录,对吧?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-02
  • 2015-04-27
  • 2015-03-17
  • 2016-01-18
  • 2015-08-28
  • 2014-03-01
相关资源
最近更新 更多