小编典典

Rails,Devise身份验证,CSRF问题

ajax

我正在使用Rails做一个单页应用程序。登录和注销时,使用ajax调用Devise控制器。我遇到的问题是当我1)登录2)退出然后再次登录时不起作用。

我认为这与CSRF令牌有关,该令牌在我退出时会重置(尽管它不应该出现),并且由于它是单页的,因此在xhr请求中发送了旧的CSRF令牌,从而重置了会话。

更具体地说,这是工作流程:

  1. 登入
  2. 登出
  3. 登录(成功201。但是WARNING: Can't verify CSRF token authenticity在服务器日志中打印)
  4. 后续ajax请求失败401未经授权
  5. 刷新网站(此时,页面标题中的CSRF更改为其他内容)
  6. 我可以登录,它可以正常工作,直到我尝试退出并再次登录。

任何线索非常感谢!让我知道是否可以添加更多详细信息。


阅读 256

收藏
2020-07-26

共1个答案

小编典典

Jimbo做得很出色,解释了您遇到的问题背后的“原因”。您可以采用两种方法来解决此问题:

  1. (如Jimbo的建议)重写Devise :: SessionsController以返回新的csrf令牌:
        class SessionsController < Devise::SessionsController
      def destroy # Assumes only JSON requests
        signed_out = (Devise.sign_out_all_scopes ? sign_out : sign_out(resource_name))
        render :json => {
            'csrfParam' => request_forgery_protection_token,
            'csrfToken' => form_authenticity_token
        }
      end
    end

并在客户端为您的sign_out请求创建成功处理程序(可能需要根据您的设置进行一些调整,例如GET vs DELETE):

        signOut: function() {
      var params = {
        dataType: "json",
        type: "GET",
        url: this.urlRoot + "/sign_out.json"
      };
      var self = this;
      return $.ajax(params).done(function(data) {
        self.set("csrf-token", data.csrfToken);
        self.unset("user");
      });
    }

这还假定您将在所有AJAX请求中自动包含CSRF令牌,如下所示:

        $(document).ajaxSend(function (e, xhr, options) {
      xhr.setRequestHeader("X-CSRF-Token", MyApp.session.get("csrf-token"));
    });
  1. 更简单地说,如果适合您的应用程序,则可以简单地覆盖Devise::SessionsController和,并使用覆盖令牌检查skip_before_filter :verify_authenticity_token
2020-07-26