【问题标题】:Rails & Oauth: multiple providersRails & Oauth:多个供应商
【发布时间】:2013-10-29 11:25:00
【问题描述】:

我有一个应用程序允许用户在 LinkedIn、Facebook 和 Twitter 上发帖。我想为每个用户的帐户授权尽可能多的这些提供商。

我的用户模型有一些列可以帮助一次授权一个提供者:

class User < ActiveRecord::Base
  ...
  attr_accessible :provider, :uid, :oauth_token, :oauth_expires_at, :oauth_token_secret, :access_token, :access_token_secret ...
  ...
end

这是模型方法:

def self.from_omniauth(user, auth)
  user.provider = auth.provider
  if auth.provider == "facebook"
    user.uid = auth.uid
    user.oauth_token = auth.credentials.token
    user.oauth_expires_at = Time.at(auth.credentials.expires_at)
  elsif auth.provider == "twitter"
    # not sure which one twitter uses
    user.oauth_token = auth["credentials"]["token"]
    user.oauth_token_secret = auth["credentials"]["secret"]
    user.access_token = auth["credentials"]["token"]
    user.access_token_secret = auth["credentials"]["secret"]
  end
  user.save!
end

而控制器的认证方法是这样的:

def authorise
  user = User.from_omniauth(current_user, env['omniauth.auth'])
  session[:user_id] = current_user.id
  redirect_to root_url
end

任何帮助都会很棒!真的不知道如何从这里开始。拥有 x(在上述情况下为 3,以后还会有更多)数量的 :provider 列似乎有点荒谬。

【问题讨论】:

    标签: ruby-on-rails facebook ruby-on-rails-3 oauth oauth-2.0


    【解决方案1】:

    关键是将身份验证部分与用户模型本身分开,这样您就可以在用户和身份之间建立has_many 关系。这是我来自旧项目的Identity 模型(使用omniauth):

    class Identity < ActiveRecord::Base
      belongs_to :user
    
      attr_accessible :provider, :uid,
                      :description, :email, :first_name, :image,
                      :last_name, :location, :name, :nickname,
                      :phone, :raw_info, :urls
    
      validates_presence_of :provider, :uid
      validates_uniqueness_of :uid, scope: :provider
    
      def self.find_with_omniauth(auth)
        find_by_provider_and_uid(auth['provider'], auth['uid'])
      end
    
      def self.create_with_omniauth(auth)
        create(provider:      auth['provider'],
                uid:          auth['uid'],
                name:         auth['info']['name'],
                email:        auth['info']['email'],
                nickname:     auth['info']['nickname'],
                first_name:   auth['info']['first_name'],
                last_name:    auth['info']['last_name'],
                location:     auth['info']['location'],
                description:  auth['info']['description'],
                image:        auth['info']['image'],
                phone:        auth['info']['phone'],
                urls:         auth['info']['urls'].to_json,
                raw_info:     auth['extra']['raw_info'].to_json
              )
      end
    end
    

    当然,用户模型应该引用:

    class User < ActiveRecord::Base
      ...
      has_many :identities, dependent: :destroy
      ...
    

    当您允许多个omniauth 提供商登录时,会出现很多令人讨厌的边缘情况。因此,要创建一个新的登录(会话),您可以执行以下操作:

    class SessionsController < ApplicationController
    
      def create
        auth = request.env['omniauth.auth']
        origin = request.env['omniauth.origin']
        destination = origin.blank? ? root_path : origin
        @identity = Identity.find_with_omniauth(auth)
        @identity = Identity.create_with_omniauth(auth) if @identity.nil?
    
        if signed_in?
          if @identity.user == current_user
            # Identity is already associated with this user
            redirect_to destination, notice: "Already logged in and linked"
          else
            # Identity is not associated with the current_user
            @old_user = @identity.user
            if @old_user
              current_user.posts << @old_user.posts
              current_user.galleries << @old_user.galleries
              current_user.favorites << @old_user.favorites
            end
            @identity.user = current_user
            @identity.save()
            @old_user.destroy if @old_user && @old_user.identities.blank?
            redirect_to destination, notice: "Account was successfully linked"
          end
        else
          if @identity.user.present?
            # Identity has a user associated with it
            self.current_user = @identity.user
            redirect_to destination
          else
            # No user associated with the identity so create a new one
            user = User.create_with_omniauth(auth['info'])
            @identity.user = user
            @identity.save()
            self.current_user = @identity.user
            redirect_to destination, notice: "Registration successful"
          end
        end
      end
    
      def destroy
        self.current_user = nil
        redirect_to root_url, notice: "Signed out successfully"
      end
    
      def omniauth_failure
        origin = request.env['omniauth.origin']
        destination = origin.blank? ? root_path : origin
        redirect_to destination, alert: "Connection failed"
      end
    end
    

    我记得,当我编写上面的代码时,我依赖于一篇关于这个主题的文章。请参阅https://github.com/intridea/omniauth/wiki/Managing-Multiple-Providers 了解更多信息和想法。

    【讨论】:

    • 感谢您真正深入的回答!期待入住。
    • 我对此的解释完全不同,但感谢您的想法。
    • 您没有说明如何处理令牌。
    • 非常好的解决方案。我个人发现这篇文章orhancanceylan.com/… 是唯一一篇真正从头到尾都有效的文章。该方法与解决方案的不同之处在于它不使用创建/销毁身份作为登录。它将它们永久留在那里,并使用这些信息通过设计登录。因此,您仍然以相同的方式登录,从而减少了不同的情况。
    • 这种方法的真正优点是它重定向 twitter 用户在第一次填写他们的电子邮件或密码,通过一个干净、彻底的解决方案,让 twitter 没有给你电子邮件的头痛消失了。
    猜你喜欢
    • 1970-01-01
    • 2014-02-10
    • 2011-01-10
    • 1970-01-01
    • 2014-12-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多