使用Devise on Rails使用单独的身份验证模型

使用Devise on Rails使用单独的身份验证模型

问题描述:

我有一个简单的解决方案,我已经使用以下对象:

I have a simple solution I've made myself with the following objects:


  • 帐户

  • 身份验证(具有auth_type / auth_id和对帐户的引用)

  • Account (has token field, that is returned when authenticating and used in API calls)
  • Authentication (has auth_type/auth_id and reference to Account)

我有一个单独的身份验证模型,可以连接多种登录方式(设备UUID,电子邮件/密码,微博,Facebook等) )。但是,在Devise的所有示例中,您都可以在用户帐户)模型中使用它。

I have a separate Authentication model to be able to connect several ways of login (device UUID, email/password, twitter, facebook etc). But it seems that in all examples of Devise you use it on the User (Account) model.

不那么灵活?例如,OmniAuth模块在用户模型中存储提供商和ID,如果您想要从Twitter和Facebook登录,会发生什么情况?只有一个供应商有空间?

Isn't that less flexible? For example OmniAuth module stores provider and id on the User model, what happens if you want to be able to login from both Twitter and Facebook, there is only room for one provider?

我应该在我的帐户模型或身份验证模型中使用Devise?

Should I use Devise on my Account model or the Authentication model?

最近,我正在使用Devise为不同服务保留用户令牌的项目。有一点不同的情况,但仍然你的问题让我想了一会儿。

Have been recently working on a project where I was using Devise to keep user's tokens for different services. A bit different case, but still your question got me thinking for a while.

我会将Devise绑定到帐户模型。为什么?让我们来看看。

I'd bind Devise to Account model anyway. Why? Let's see.

由于我的电子邮件是唯一可以将用户识别为我的用户(并且您将用户名称帐户)指定给我在帐户表中配对密码,以便我最初能够使用基本的电子邮件/密码身份验证。另外我还会在认证中保留API令牌

Since my email is the only thing that can identify me as a user (and you refer to Account as the User) I would place it in accounts table in pair with the password, so that I'm initially able do use basic email/password authentication. Also I'd keep API tokens in authentications.

如前所述,OmniAuth模块需要存储提供商和id。如果您希望您的用户能够同时与不同的服务连接(并且由于某种原因您可以),那么很明显,您需要将两个provider-id对保持在某处,否则将简单地每次单个用户认证时都会被覆盖。这导致了我们已经适合的认证模型,并引用了帐户

As you've mentioned, OmniAuth module needs to store provider and id. If you want your user to be able to be connected with different services at the same time (and for some reason you do) then obviously you need to keep both provider-id pairs somewhere, otherwise one will simply be overwritten each time a single user authenticates. That leads us to the Authentication model which is already suitable for that and has a reference to Account.

所以在寻找provider-id对时,您需要检查认证表而不是帐户。如果找到,您只需返回一个帐户。如果不是,请检查是否存在包含此类电子邮件的帐户。创建新的认证如果答案为是,否则创建一个,然后为其创建认证

So when looking for a provider-id pair you want to check authentications table and not accounts. If one is found, you simply return an account associated with it. If not then you check if account containing such email exists. Create new authentication if the answer is yes, otherwise create one and then create authentication for it.

更具体地说:

#callbacks_controller.rb
controller Callbacks < Devise::OmniauthCallbacksContoller
  def omniauth_callback
    auth = request.env['omniauth.auth']
    authentication =  Authentication.where(provider: auth.prodiver, uid: auth.uid).first
    if authentication
      @account = authentication.account
    else
      @account = Account.where(email: auth.info.email).first
      if @account
        @account.authentication.create(provider: auth.provider, uid: auth.uid,
         token: auth.credentials[:token], secret: auth.credentials[:secret])
      else
        @account = Account.create(email: auth.info.email, password: Devise.friendly_token[0,20])
        @account.authentication.create(provider: auth.provider, uid: auth.uid,
         token: auth.credentials[:token], secret: auth.credentials[:secret])
      end
    end
    sign_in_and_redirect @account, :event => :authentication
  end
end

#authentication.rb
class Authentication < ActiveRecord::Base
  attr_accessible :provider, :uid, :token, :secret, :account_id
  belongs_to :account
end

#account.rb
class Account < ActiveRecord::Base
  devise :database_authenticatable
  attr_accessible :email, :password
  has_many :authentications
end

#routes.rb
devise_for :accounts, controllers: { omniauth_callbacks: 'callbacks' }
devise_scope :accounts do
  get 'auth/:provider/callback' => 'callbacks#omniauth_callback'
end

这应该给你所需要的,同时保持灵活性你想要。

That should give you what you need while keeping the flexibility you want.