这是我们的扩展的sessions_controller代码.它适用于成功和失败的登录尝试:
# CUSTOM (Mix of actual devise controller method and ajax customization from http://natashatherobot.com/devise-rails-sign-in/): def create # (NOTE: If user is not confirmed,execution will never get this far...) respond_to do |format| format.html do # Copied from original create method: self.resource = warden.authenticate!(auth_options) set_flash_message(:notice,:signed_in) if is_navigational_format? sign_in(resource_name,resource) respond_with resource,:location => after_sign_in_path_for(resource) end format.js do # Derived from Natasha AJAX recipe: self.resource = warden.authenticate!(:scope => resource_name,:recall => "#{controller_path}#failure") sign_in(resource_name,resource) return render :json => {:success => true,:token => form_authenticity_token() },content_type: "application/json" # Need to explicitely set content type to JSON,otherwise it gets set as application/javascript and success handler never gets hit. end end end def failure return render :json => {:success => false,:errors => ["Login Failed."]} end
问题是,如果用户未经确认,则create方法永远不会被命中.重定向发生在之前的某个地方,这意味着我们无法以JS友好的方式处理它.但是通过源代码我找不到任何可以进行确认检查的过滤器.确认检查在哪里发生,我们如何拦截它?
解决方法
如果你看一下lib / devise / controllers / helpers.rb中sign_in的定义,你会发现,在你第一次登录用户的正常流程中,你最终会调用
warden.set_user(resource,options.merge!(:scope => scope)
warden是对Warden :: Proxy对象的引用,如果你看一下set_user的作用(你可以在warden/lib/warden/proxy.rb:157-182
看到),你会看到在将用户序列化到会话后它会运行任何after_set_user回调.
Devise在lib / devise / hooks /中定义了一堆这些,我们感兴趣的特定的是lib / devise / hooks / activatable.rb:
Warden::Manager.after_set_user do |record,warden,options| if record && record.respond_to?(:active_for_authentication?) && !record.active_for_authentication? scope = options[:scope] warden.logout(scope) throw :warden,:scope => scope,:message => record.inactive_message end end
如您所见,如果记录不是active_for_authentication?,那么我们抛出.这就是你的情况 – active_for_authentication?对于尚未确认的可确认资源,返回false(参见lib/devise/models/confirmable.rb:121-127
).
当我们抛出:warden时,我们最终会调用devise failure_app.这就是正在发生的事情,以及为什么你打破了控制器的正常控制流程.
(实际上上面是谈论正常的会话控制器流程.我认为你的js块实际上是多余的 – 调用warden.authenticate!也会设置用户,所以我认为你甚至在你登录之前就扔了.)
要回答第二个问题,处理此问题的一种可能方法是创建自己的失败应用程序.默认设置将warden的failure_app设置为Devise :: Delegator,它允许您为不同的设计模型指定不同的故障应用程序,但如果没有配置任何设备,则默认为Devise :: FailureApp.您可以自定义现有的故障应用程序,通过配置warden将其替换为您自己的故障应用程序,或者您可以自定义委托者以使用html请求的默认故障应用程序并委托给json的其他故障应用程序.