Devise + OmniAuth 第三方登录

28 May 2017

原理图

这逻辑图建议大家开始之前看一遍 做完以后再看一遍 相信再不会有什么问题

准备工作

配置Gem

  1. 在Gemfile中加入以下gem

     # Gemfile
     gem 'omniauth'
     gem 'omniauth-google-oauth2'
     gem 'omniauth-facebook'
     gem 'omniauth-github'
     gem 'figaro'
    
  2. bundle install
  3. figaro install

给User加Username

  1. rails generate migration add_username_to_users username:string:uniq
  2. rake db:migrate
  3. 在User.rb中

     # user.rb
     validates :username, presence: true, length: {maximum: 25}
    
  4. 在ApplicationController.rb中填入一下内容

     class ApplicationController < ActionController::Base
       before_action :configure_permitted_parameters, if: :devise_controller?
    
       protected
    
       def configure_permitted_parameters
         added_attrs = [:username, :email, :password, :password_confirmation, :remember_me]
         devise_parameter_sanitizer.permit :sign_up, keys: added_attrs
         devise_parameter_sanitizer.permit :account_update, keys: added_attrs<<:current_password
       end
     end
    

    这里注意,把protected以及它后面的内容都写在最后面

给User加Image

  1. rails g migration add_image_to_users image:string
  2. rake db:migrate

创建一个叫Identify的model

  1. rails g model identify user_id:integer provider:string uid:string
  2. 在User.rb中,填入

     # user.rb
     has_many :identifies
    
  3. 在Identify.rb中,填入

     # identify.rb
     belongs_to :user
    

    现在user与identify形成了一对多的关系(举例:一个user对应一个google的identify,一个facebook的identify,一个gihub的identify))

  4. rake db:migrate

实现Google登录

配置路由生成authorize与callback两个网址

  1. 在User.rb中的devise 一行里添加一个:omniauthable

     # user.rb
     devise :database_authenticatable, :registerable,
              :recoverable, :rememberable, :trackable, :validatable,
              :omniauthable
    

    注意:validatable后面的逗号不要掉了

  2. 在devise.rb中填入

     # devise.rb
     require 'omniauth-google-oauth2'
    
     config.omniauth :google_oauth2, ENV["GOOGLE_CLIENT_ID"], ENV["GOOGLE_CLIENT_SECRET"], {access_type: "offline", approval_prompt: ""}
    
  3. 在终端中输入rake routes你就可以看到,它们做产生的两个访问网址

这个user_google_oauth2_omniauth_authorize的处理已经由devise+omniauth帮我们搞定了, 剩下这个user_google_oauth2_omniauth_callback需要我们自己建立一个controller来处理 所以下面就是来建立这个callback回调controller

建立callback回调controller

  1. 在routes.rb中的devise_for :users后面填上如下内容

     # routes.rb
     devise_for :users, :controllers => {:omniauth_callbacks => "omniauth_callbacks"}
    

    目的是让routes知道,未来当devise接受到认证回调消息的时候,由一个叫omniauth_callbacks的controller来处理(下一步我们就会创建它)

  2. 在终端输入rails g controller omniauth_callbacks

  3. 去到这个新建的omniauth_callbacks_controller.rb中,填入以下内容

# omniauth_callbakcs_controller.rb
def google_oauth2
  @user = User.from_google(request.env["omniauth.auth"], current_user)

  if @user.persisted?
    flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Google"
    sign_in_and_redirect @user, :event => :authentication
  else
    session["devise.user_data"] = request.env["omniauth.auth"]
    redirect_to new_user_registration_path
  end
end
注意User又一个from_google的方法,我们还没有定义,下面就去定义

定义User的from_google方法

在User.rb中填入以下内容

  # user.rb
  def self.from_google(access_token, signed_in_resource=nil)
    data = access_token.info
    identify = Identify.find_by(:provider => access_token.provider, :uid => access_token.uid)

    if identify
      return identify.user
    else
      user = User.find_by(:email => access_token.email)
      if !user
        user = User.create(
          username: data["name"],
          email: data["email"],
          image: data["image"],
          password: Devise.friendly_token[0,20]
        )
      end
      identify = Identify.create(
        provider: access_token.provider,
        uid: access_token.uid,
        user: user
      )
    return user
    end
  end

在Google开发者平台上建立应用并拿到app_id与secret_key

  1. 登录Google开发者平台 Google Cloud Platform

  2. 创建一个应用

  3. 起个名字点击创建

  4. 确保在自己新建的项目中,并点击左侧Library,然后分别去往Contacts API与Google+ API中,点选enable

  5. 设置product名称

  6. 点击左侧Credentials然后点选中间Create credentials下拉菜单中的OAuth client ID

  7. 填表单相应需要的内容 更正:

    注意,第四步里,建议填入两个 第一个是本地开发用的http://localhost:3000/users/auth/google_oauth2/callback 第二个是你自己heroku项目的https回调网址https://meizhebao.herokuapp.com/users/auth/google_oauth2/callback 第三个是你自己heroku项目的http回调网址http://meizhebao.herokuapp.com/users/auth/google_oauth2/callback

  8. 获得Client ID与Client Secret 你可自己拷贝记录下来,也可以放在这里,我们马上就要使用

用figaro来管理你的google client id 与 secret key

  1. 在config/application.yml中填入以下内容

     # application.yml
     production:
       GOOGLE_CLIENT_ID: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
       GOOGLE_CLIENT_SECRET: 'xxxxxxxxxxxxxxx'
     development:
       GOOGLE_CLIENT_ID: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
       GOOGLE_CLIENT_SECRET: 'xxxxxxxxxxxxxxx'
    
  2. 把你刚才拿到的client ID 与 client secret分别去替换application.yml中的内容

    现在controller,routes,model都配置完毕,可以去view中放一个用google登录注册的按钮了

创建Google一键登录按钮

  1. 终端中输入rails g devise:views
  2. 在app/views/devise/registrations/new.html.erb中 Sign Up标题的下面填入一行代码

     # app/views/devise/registrations/new.html.erb
     <%= link_to "用Google登录", user_google_oauth2_omniauth_authorize_path, class: "btn btn-primary" %>
    

大功告成

现在就可以去试一试google登录了

实现用Facebook登录

前面已实现的内容

Gemfile中引用gem 'omniauth-facebook'等 已经给user添加了usernmae 已经创建identify 已经给user添加image 下面略微有一点不同,就照老步骤来

配置路由生成authorize与callback两个网址

  1. 【之前已实现,可跳过】在User.rb中的devise 一行里添加一个:omniauthable
  2. 在devise.rb中填入

       # devise.rb
       require 'omniauth-facebook'
       config.omniauth :facebook, ENV["FACEBOOK_CLIENT_ID"], ENV["FACEBOOK_CLIENT_SECRET"]
    
  3. 在终端中输入rake routes你就可以看到,新产生的两个facebook访问验证网址 这个user_github_omniauth_authorize的处理已经由devise+omniauth帮我们搞定了, 剩下这个user_github_omniauth_callback需要我们自己建立一个controller来处理 所以下面就是来建立这个callback回调controller

建立callback回调controller

  1. 【之前已实现,就可跳过】在routes.rb中的devise_for :users后面填上如下内容

       # routes.rb
       devise_for :users, :controllers => {:omniauth_callbacks => "omniauth_callbacks"}
    

    目的是让routes知道,未来当devise接受到认证回调消息的时候,由一个叫omniauth_callbacks的controller来处理(下一步我们就会创建它)

  2. 【之前已实现,可跳过】在终端输入rails g controller omniauth_callbacks
  3. 在这个新建的omniauth_callbacks_controller.rb中,填入以下内容

       # omniauth_callbacks_controller.rb
       def facebook
         @user = User.from_facebook(request.env["omniauth.auth"], current_user)
    
         if @user.persisted?
           flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Facebook"
           sign_in_and_redirect @user, :event => :authentication
         else
           session["devise.user_data"] = request.env["omniauth.auth"]
           redirect_to new_user_registration_path
         end
       end
    

    注意User有一个from_github的方法,我们还没有定义,下面就去定义

定义User的from_facebook方法

在User.rb中填入以下内容

  # user.rb
  def self.from_facebook(access_token, signed_in_resoruce=nil)
    data = access_token.info
    identify = Identify.find_by(provider: access_token.provider, uid: access_token.uid)
  
    if identify
      return identify.user
    else
      user = User.find_by(:email => data.email)
      if !user
        user = User.create(
          username: access_token.extra.raw_info.name,
          email: data.email,
          image: data.image,
          password: Devise.friendly_token[0,20]
        )
      end
      identify = Identify.create(
        provider: access_token.provider,
        uid: access_token.uid,
        user: user
      )
      return user
    end
  end

在facebook开发者平台上建立应用并拿到app_id与secret_key

  1. 登录 facebook开发者平台
  2. 创建一个新应用
  3. 开启facebook登录的功能
  4. 输入项目和localhost地址

  5. 拿到client id与client secret
  6. 公开你的应用

用figaro来管理你的facebook client id 与 secret key

  1. 在config/application.yml中填入以下内容

     # config/application.yml
     production:
       GOOGLE_CLIENT_ID: 'oooooooooooooooooooooooooooooooooo'
       GOOGLE_CLIENT_SECRET: 'oooooooooooo'
       FACEBOOK_CLIENT_ID: 'xxxxxxxxxxx'
       FACEBOOK_CLIENT_SECRET: 'xxxxxxxxxxxxxxxxxxxxxxxx'
     development:
       GOOGLE_CLIENT_ID: 'oooooooooooooooooooooooooooooooooo'
       GOOGLE_CLIENT_SECRET: 'oooooooooooo'
       FACEBOOK_CLIENT_ID: 'xxxxxxxxxxx'
       FACEBOOK_CLIENT_SECRET: 'xxxxxxxxxxxxxxxxxxxxxxxx'
    
  2. 把你刚才拿到的client ID 与 client secret分别去替换application.yml中的FACEBOOK_CLIENT_ID与FACEBOOK_CLIENT_SECRET的内容

    现在controller,routes,model都配置完毕,可以去view中放一个用gihub登录注册的按钮了

创建Facebook一键登录按钮

  1. 【以实现,可跳过】终端中输入rails g devise:views
  2. 在app/views/devise/registrations/new.html.erb中 Sign Up标题的下面填入一行代码

     # app/views/devise/registrations/new.html.erb
     <%= link_to "用Facebook登录", user_facebook_omniauth_authorize_path, class: "btn btn-info" %>
    

大功告成

现在就可以去试一试google登录了

实现用Github登录

前面已实现的内容

Gemfile中引用gem 'omniauth-github' 已经给user添加了usernmae 已经创建identify 已经给user添加image 下面略微有一点不同,就照老步骤来

配置路由生成authorize与callback两个网址

  1. 在User.rb中的devise 一行里添加一个:omniauthable 这一步前文已做,所以跳过

  2. 在devise.rb中填入

     # devise.rb
     require 'omniauth-github'
     config.omniauth :github, ENV["GITHUB_CLIENT_ID"], ENV["GITHUB_CLIENT_SECRET"], scope: "user:email"
    
  3. 在终端中输入rake routes你就可以看到,新产生的两个github访问验证网址 这个user_github_omniauth_authorize的处理已经由devise+omniauth帮我们搞定了, 剩下这个user_github_omniauth_callback需要我们自己建立一个controller来处理 所以下面就是来建立这个callback回调controller

建立callback回调controller

  1. 【之前已实现,就可跳过】在routes.rb中的devise_for :users后面填上如下内容

     # routes.rb
     devise_for :users, :controllers => {:omniauth_callbacks => "omniauth_callbacks"}
    

    目的是让routes知道,未来当devise接受到认证回调消息的时候,由一个叫omniauth_callbacks的controller来处理(下一步我们就会创建它)

  2. 【已实现,可跳过】在终端输入rails g controller omniauth_callbacks

  3. 在这个新建的omniauth_callbacks_controller.rb中,填入以下内容

     # omniauth_callbacks_controller.rb
     def github
       @user = User.from_github(request.env["omniauth.auth"], current_user)
    
       if @user.persisted?
         flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Github"
         sign_in_and_redirect @user, :event => :authentication
       else
         session["devise.user_data"] = request.env["omniauth.auth"]
         redirect_to new_user_registration_url
       end
     end
    

    注意User有一个from_github的方法,我们还没有定义,下面就去定义

定义User的from_github方法

在User.rb中填入以下内容

# user.rb
def self.from_github(access_token, signed_in_resoruce=nil)
  data = access_token["info"]
  identify = Identify.find_by(provider: access_token["provider"], uid: access_token["uid"])
  
  if identify
    return identify.user
  else
    user = User.find_by(:email => data["email"])
  
    if !user
  
      if data["name"].nil?
        name = data["nickname"]
      else
        name = data["name"]
      end
  
      user = User.create(
        username: name,
        email: data["email"],
        image: data["image"],
        password: Devise.friendly_token[0,20]
      )
    end
  
    identify = Identify.create(
      provider: access_token["provider"],
      uid: access_token["uid"],
      user: user
    )
  
    return user
  end
end

在Github后台上建立应用并拿到app_id与secret_key

  1. 进入Github设置
  2. 创建一个新的应用
  3. 填入相应的名字和网址以及回调网址

    这里注意,将来你上传到heroku上以后,要把这两个地址换成你项目的地址和项目的回调地址

  4. 注册后就可以拿到Client ID与Client Secret了

用figaro来管理你的github client id 与 secret key

  1. 在config/application.yml中填入以下内容

     # config/application.yml
     production:
       GOOGLE_CLIENT_ID: 'oooooooooooooooooooooooooooooooooo'
       GOOGLE_CLIENT_SECRET: 'oooooooooooo'
       FACEBOOK_CLIENT_ID: 'oooooooooooo'
       FACEBOOK_CLIENT_SECRET: 'oooooooooooooooooooooooooooooo'
       GITHUB_CLIENT_ID: 'xxxxxxxxxxxxxxx'
       GITHUB_CLIENT_SECRET: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
     development:
       GOOGLE_CLIENT_ID: 'oooooooooooooooooooooooooooooooooo'
       GOOGLE_CLIENT_SECRET: 'oooooooooooo'
       FACEBOOK_CLIENT_ID: 'oooooooooooo'
       FACEBOOK_CLIENT_SECRET: 'oooooooooooooooooooooooooooooo'
       GITHUB_CLIENT_ID: 'xxxxxxxxxxxxxxx'
       GITHUB_CLIENT_SECRET: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
    
  2. 把你刚才拿到的client ID 与 client secret分别去替换application.yml中的GITHUB_CLIENT_ID与GITHUB_CLIENT_SECRET的内容

    现在controller,routes,model都配置完毕,可以去view中放一个用gihub登录注册的按钮了

创建Gighub一键登录按钮

  1. 【以实现,可跳过】终端中输入rails g devise:views

  2. 在app/views/devise/registrations/new.html.erb中 Sign Up标题的下面填入一行代码

     # app/views/devise/registrations/new.html.erb
     <%= link_to "Sign up with Github", user_github_omniauth_authorize_path, class: "btn btn-danger" %>
    

大功告成

现在就可以去试一试Github登录了

最后的最后

记住把项目上传到heroku上之后 要通过输入figaro heroku:set -e production 把所有id和secret_key都上传到heroku上才能使用

核心代码解析

参考资料

devise wiki Rails4 omniauth using devise with twitter facebook and linkedin How To Setup Devise and OmniAuth for Your Rails Application devise wiki