ruby-on-rails - rails 与 Oauth : multiple providers

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

我有一个允许用户在 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

Controller 的 auth 方法如下所示:

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 列似乎有点荒谬。

最佳答案

关键是将身份验证部分与用户模型本身分开,这样您就可以在用户和身份之间建立一个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 提供程序登录时,会出现很多讨厌的边缘情况。所以要创建一个新的登录( session ),你可以这样做:

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了解更多信息和想法。

关于ruby-on-rails - rails 与 Oauth : multiple providers,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19486567/

相关文章:

mysql - 如何通过单个 SQL 查询检索同一个数据库表中的 "common"条记录?

sql - 如何使用 Arel 正确地向带有 'or' 和 'and' 子句的 SQL 查询添加括号?

ruby-on-rails - 如何将现有的旧 Rails 项目更新为新版本

ajax - 如何使用 hashbang url 处理 facebook 分享/点赞?

mysql - Facebook 登录页面和缓存

ruby - 在多台服务器上重新请求工作人员

ruby-on-rails - Ruby on Rails map.root 似乎无法正常工作

ruby-on-rails - 如何查询delay_job处理程序

c# - ASP.NET 中的简单 Facebook 连接演示

ruby-on-rails - rails : How do I search tags generated by Act_As_Taggable_On with PG_Search? (postgresql)