Rails和ajax请求:不使用csrf工作?

时间:2012-04-30 13:32:55

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

我正在使用此代码向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 for devise:

  $.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!从控制器中取代了与另一个相关的create_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保护,但我仍然得到相同的错误...所以probelm是另一个但我真的无法理解什么会导致双重定向,因为在数据库中正确创建了记录...

2 个答案:

答案 0 :(得分:2)

如果您已将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 :(得分:0)

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

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