使用Omniauth为多个模型开发Devise而不使用STI

9
有没有一种方法可以为多个模型配置Devise Omniauth而不使用STI?
我们有学生和教授模型,我们不想使用STI,但现在我们意识到Devise与Omniauth不能很好地处理多个模型。
.rvm/gems/ruby-1.9.3-p125/gems/devise-2.1.0/lib/devise/rails/routes.rb:384:in `devise_omniauth_callback': Wrong OmniAuth configuration. If you are getting this exception, it means that either: (RuntimeError)

1) You are manually setting OmniAuth.config.path_prefix and it doesn't match the Devise one
2) You are setting :omniauthable in more than one model
3) You changed your Devise routes/OmniAuth setting and haven't restarted your server

1
我也遇到了同样的问题。我目前正在考虑手动连接Facebook身份验证。 =/ - Ray Shih
我们选择了一个名为User的父类,该类使用Devise Omniauth进行身份验证,然后其他模型从User继承。没有找到其他更好的选择... - marc_ferna
@marc_ferna,如果你的代码不做一些逻辑上的修改,它是不能直接运行的。 - Sandip Ransing
4个回答

3

目前,Devise的Omniauthable模块不支持多个模型。 (https://github.com/plataformatec/devise/wiki/OmniAuth-with-multiple-models)

简而言之,您需要停止依赖devise通过“omniauthable”实现其魔法,并手动完成此操作。这并不复杂,一旦您让第一个模型通过中间件正常工作,那么扩展到多个模型就很明显了,因为它归结为完全相同的代码,只是有不同的模型名称。

所需步骤:

  1. 从devise.rb和使用的模型中删除omniauthable和devise omniauth设置
  2. 包含oauth作为中间件(因此您可以在请求到达Rails之前捕获它)
  3. 编写手动路由-非常容易
  4. 处理响应-几乎与您现在拥有的代码相同
  5. 处理失败场景(在oauth提供程序上被拒绝)-中间件设置中的另一个块
我曾解决类似问题,并在此提供详细的说明:https://blog.kodius.io/2016/12/20/devise-omniauth-multiple-models/ 中间件oauth设置的代码在这里:https://github.com/kodius/oauth-example-multiple-models

3

您好,我遇到了一个类似的问题,并找到了以下解决方案,希望能帮助下一个人。

我的情况是有两个设备模型,我用不同的方式使用omniauth,在第一个模型中,用户可以通过普通的devise注册或omniauth进行登录。在第二个模型中,艺术家只能通过常规注册表单进行注册,但我仍然需要omniauth来验证推特上的值,即已验证字段(推特资料上的小勾号,表示著名用户确实是该人)。

因此,当我发现无法拥有两个devise omniauthable模型时,我决定同时使用相同的omniauth。

class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
  def all
    if artist_signed_in? and session["devise.authorize"] == current_artist.email
      session["devise.authorize"] = nil
      if current_artist.artist_profile.update_from_omniauth(request.env["omniauth.auth"])
        if current_artist.artist_profile.verified
          flash[:notice] = "You were verified"
        else
          flash[:alert] = "Twitter go well but you aren't verified there"
        end
        redirect_to edit_artist_profile_path(current_artist.artist_profile)
      else
        flash[:error] = "We can't connect with #{request.env["omniauth.auth"].provider.titleize.split(" ").first}"
        redirect_to edit_artist_profile_path(current_artist.artist_profile)
      end
    elsif !user_signed_in?
      user = User.from_omniauth(request.env["omniauth.auth"])
      if user.persisted?
        flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => user.provider.titleize.split(" ").first
        user.user_profile.from_omniauth(request.env["omniauth.auth"])
        sign_in_and_redirect user, :event => :authentication
      else
        session["count_errors"] = 0 if session["devise.user_attributes"] == nil
        session["devise.user_attributes"] = user.attributes
        redirect_to new_user_registration_url
      end
    else
      flash[:notice] = "You are already log in #{current_user.email}"
      redirect_to root_path
    end
  end

  def after_omniauth_failure_path_for(scope)
    if artist_signed_in? and session["devise.authorize"] == current_artist.email
      session["devise.authorize"] = nil
      edit_artist_profile_path(current_artist.artist_profile)
    else
      super
    end
  end

  alias_method :twitter, :all
end

对于第一种情况,普通用户,我们有以下方案:

elsif !user_signed_in?

对于正常的流程,所有的指南都会进行,但是对于第二种情况(验证字段和艺术家档案),我会发送一个带有随机值的小会话。

session["devise.authorize"]

我从我的艺术家资料控制器中使用新路由调用链接。

<%= link_to "Verify with Twitter", artist_profiles_integrate_twitter_path %>

谁加载会话并重定向到用户omniauth路由?
class ArtistProfilesController < ApplicationController
   ...
   def integrate_twitter
      session["devise.authorize"] = current_artist.email
      redirect_to user_omniauth_authorize_path(:twitter)
   end
 end

然后在每个类中定义了几种处理omniauth的方法,第一种是基于railscast教程“devise-omniauth-revised”创建用户,第二种只是更新艺术家资料模型上的字段。你应该覆盖after_omniauth_failure_path_for(scope)方法,它返回登录失败的路径。使用相同的技术,你可以更改错误后的路径(例如连接Twitter失败时将重定向到用户注册路径,并在一段时间内维持会话)。通过这种方式我们可以实现正常的行为并在所有情况下清除会话。希望能对您有所帮助。祝好!

2

1

我认为,与其在每个StudentProfessor model中编写:omniauthable,不如生成第三个模型,例如Omniuser,并将:omniauthable设置添加到其中。

class Student < ActiveRecord::Base
  has_one :omniuser
end

class Professor < ActiveRecord::Base
  has_one :omniuser
end

这是我们最终采用的方法,但它是单表继承,而我们本来想避免这种情况。 - marc_ferna
@marc_ferna,实际上我并不建议使用STI方法,而是建议使用另一个omniuser模型来保存ominiauth相关内容。 - Sandip Ransing
谢谢您的回答。也许我误解了STI或者您的解决方案,但是根据这篇文章,这个解决方案将涉及到模型omniauth,它将拥有属性emailpassword等,以及模型student,它将有自己的属性。这两个模型(和它们的属性)将成为我们数据库中的一个单一表格(professor同理)。我的理解正确吗?如果我误解了您的回答,请进一步解释一下 ;) - marc_ferna
@marc_ferna - 你能详细说明一下你是如何为STI创建设备与omniauth模型的吗?我们有三个独立的用户模型,其中两个需要omniauth-facebook。对于模型和路由设置感到非常困惑。 - Sandeep

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接