Rails / Devise登录不适用于Safari(422 / CSRF错误)

时间:2016-12-07 20:45:15

标签: ruby-on-rails devise safari csrf

登录在Chrome上正常运行,但在Safari(或我假设其他Webkit浏览器)上无效。登录后我收到此错误消息("您想要的更改被拒绝。也许您尝试更改了您无法访问的内容。"):

The change you wanted was rejected. Maybe you tried to change something you didn't have access to.

根据我的heroku日志,这就是发生的事情:

2016-12-07T14:14:23.778153+00:00 app[web.1]: Can't verify CSRF token authenticity
2016-12-07T14:14:23.778899+00:00 app[web.1]: Completed 422 Unprocessable Entity in 2ms (ActiveRecord: 0.0ms)
2016-12-07T14:14:23.785544+00:00 app[web.1]:
2016-12-07T14:14:23.785547+00:00 app[web.1]: ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticityToken):

我相信我发送了正确的CSRF令牌,但似乎有些事情发生了故障。这是我目前的application_controller.rb

class ApplicationController < ActionController::Base


  # Prevent CSRF attacks by raising an exception.
  # For APIs, you may want to use :null_session instead.
  protect_from_forgery with: :exception

  after_action :flash_to_headers

  # this is so that json requests don't redirect without a user
  before_action :authenticate_user!
  # before_action :authenticate_user!, unless: request.format == :json
  # before_action :user_needed, if: request.format == :json

  before_action :set_paper_trail_whodunnit
  before_action :set_global_search_variable


  def set_global_search_variable
    @q = Person.ransack(params[:q])
  end

  def user_needed
    unless current_user
      render json: { 'error' => 'authentication error' }, status: 401
    end
  end



  def flash_to_headers
    return unless request.xhr?
    response.headers['X-Message'] = flash_message if flash_message
    response.headers['X-Message-Type'] = flash_type.to_s if flash_type
    flash.discard # don't want the flash to appear when you reload page
  end

  private

    def flash_message
      [:error, :warning, :notice].each do |type|
        return flash[type] unless flash[type].blank?
      end
      nil
    end

    def flash_type
      [:error, :warning, :notice].each do |type|
        return type unless flash[type].blank?
      end
      nil
    end

(将protect_from_forgery with:更改为null_session只会导致无限循环返回登录屏幕。)

This question引用了类似的问题,但没有讨论Devise的复杂性。据说Devise已经解决了这个问题,但它在某种程度上并没有在这里工作。许多these answers已有多年的历史,所以我不确定它们今天会有多相关。

我还尝试在实际的Devise Github回购中搜索错误,但我似乎无法在这些主题中获得任何建议。编辑应用程序控制器的建议很多,但很多时候似乎会破坏整个应用程序。

这个应用程序运行Ruby 2.2.5和Rails 4.2.7.1。更新到Rails 5是否有助于解决这个问题?

它还有一个用于制作管理员帐户的现有(也可能是hacky)覆盖;此人通过Devise注册,然后通过approved shell中的另一个名为pqsl的字段获得管理员访问权限。我不确定这是否相关。

该应用程序在Github,对于任何想看一眼的人: https://github.com/yamilethmedina/kimball

2 个答案:

答案 0 :(得分:7)

事实证明,this answer解决了我的问题。毕竟它不在应用程序控制器中,而是在config/initializers/session_store.rb

这是我最初的session_store

Logan::Application.config.session_store :cookie_store, key: '_cutgroup_session', secure: (Rails.env.production? || Rails.env.staging?)

进一步研究后,我发现了这个建议:

Rails.application.config.session_store :cookie_store, key: "_rails_session_#{Rails.env}", domain: all

这仍然无效;但是,它会在日志中产生401错误(而不是422)并重定向回登录页面,而不是显示我在上面截屏的错误屏幕。

最后,我在Safari上为我工作的domain: all的末尾删除了Rails.application.config.session_store :cookie_store, key: "_rails_session_#{Rails.env}"部分(Cookie在浏览器的任何位置都没有被阻止)。现在,我可以在Heroku上部署项目时登录。

赏金是一个沉重的代价,但至少评论者帮助我澄清了我的想法并找到了解决方案!如果其他人遇到这个问题并提出了一个更好的问题,我会反过来投票,但我想我现在已经得到了它。

答案 1 :(得分:1)

尝试:

<强>控制器/ application.rb中

protect_from_forgery with: :null_session

和 覆盖您的设备控制器

<强> sessions_controller.rb

class Users::SessionsController < Devise::SessionsController
skip_before_filter :verify_authenticity_token, :only => [:destroy]
end