Omniauth facebook会话控制器在Michael Hartl教程之后创建动作集成

时间:2013-12-21 13:56:24

标签: ruby-on-rails omniauth railstutorial.org

我刚刚完成了Michael Hartl教程。我正在尝试实施omniauth-facebook注册和登录用户。我在创建要在会话控制器中的create操作中使用的主变量时遇到问题。我的假设是我应该输入一个if else声明来查看主人是通过Facebook登录还是通过默认表格登录?或者我应该使用and or声明:

(master = Master.find_by(email: params[:session][:email].downcase) || Master.from_omniauth(env["omniauth.auth"])) ?

这是omniauth-facebook sessons控制器创建动作代码:

def create
    user = User.from_omniauth(env["omniauth.auth"])
    session[:user_id] = user.id
    redirect_to root_url
  end

这是我当前的会话控制器创建动作代码:

class SessionsController < ApplicationController

    def new
    end

    def create
        master = Master.find_by(email: params[:session][:email].downcase)
        if master && master.authenticate(params[:session][:password])
            sign_in master
            redirect_back_or master
        else
            flash.now[:error] = 'Invalid email/password combination'
            render 'new'
        end
    end
    .
    .
    .


end

3 个答案:

答案 0 :(得分:5)

虽然我可能迟到一年才能回答这个问题,但我仍然认为像一年前一样出现类似问题的新观众可能会受益于以下答案

据我所知,实际问题是:

  

如何将 Facebook身份验证与从Scratch 构建的身份验证系统集成?

既然你说过你使用过迈克尔·哈特尔的教程,我就继续调整了迈克尔自己编写的认证系统的答案。现在让我们一步一步地遍历每一段代码。

1

首先,我们需要为提供商 uid 的现有用户表创建两个额外的列,如下所示...

rails g migration add_facebook_auth_to_users provider uid

...并将omniauth-facebook gem添加到Gemfile ...

<强>的Gemfile

gem 'omniauth-facebook', '2.0.1'

不要忘记运行bundle install并在之后迁移您的数据库。

2

接下来我们需要继续编辑我们的会话控制器......

<强> sessions_controller.rb

def create
  user = User.find_by(email: params[:session][:email].downcase)
  if user && user.authenticate(params[:session][:password])
    if user.activated?
      log_in user
      params[:session][:remember_me] == '1' ? remember(user) : forget(user)
      redirect_back_or user
    else
      message  = "Account not activated. "
      message += "Check your email for the activation link."
      flash[:warning] = message
      redirect_to root_url
    end
  else
    flash.now[:danger] = 'Invalid email/password combination'
    render 'new'
  end
end

def create_facebook
  user = User.from_omniauth(env["omniauth.auth"])
  log_in user
  redirect_back_or user 
end

上面的会话控制器包含两个创建操作。第一个是直接从书中获取的未触及的方法,当第二个是我专门用于Facebook身份验证的一段代码时。

user = User.from_omniauth(env["omniauth.auth"])有两个目的。如果uid存在,则创建用户 IF 他/她的唯一facebook uid不在数据库中 ELSE 在应用中记录此人。稍后会详细介绍。

3

接下来,让我们创建一个有效的 .from_omniauth 方法,您可以在问题中简要地显示代码内部......

<强> user.rb

def self.from_omniauth(auth)
  where(provider: auth.provider, uid: auth.uid).first_or_initialize.tap do |user|
    user.provider = auth.provider
    user.uid = auth.uid
    user.name = auth.info.name unless user.name != nil
    user.email =  SecureRandom.hex + '@example.com' unless user.email != nil
    user.activated = true
    user.password = SecureRandom.urlsafe_base64 unless user.password != nil
    user.save!
  end
end

在这里,我们尝试找到与哈希值中的给定provideruid值匹配的用户,然后我们就此结果调用first_or_initialize。此方法将返回第一个匹配记录或使用传入的参数初始化新记录。然后,我们调用tap将该User实例传递给块。在块内部,我们根据OmniAuth哈希值设置用户的各种属性。

您可能会对为什么在某些属性初始化后放置unless语句感兴趣。请考虑用户想要更新配置文件的情况,例如更改名称或smth,然后注销。当该用户最终决定重新登录时,.from_omniauth方法会将有问题的用户更新为原始的facebook值,除非我们停止这样做。< / p>

另请注意SecureRandom库的使用。在Michael Hartl在他的书中使用的传统身份验证中,他介绍了对电子邮件和密码提交的验证。电子邮件既不是空白也不是空白。同样,密码长度必须大于6个字符。由于电子邮件必须存在且独特,我决定使用SecureRandom.hex + @example.com创建虚拟电子邮件。这将创建一个随机十六进制字符串,如52750b30ffbc7de3b362,并将其附加到@example.com,从而生成唯一的虚拟电子邮件。密码相同,但我更喜欢使用SecureRandom.urlsafe_base64生成随机base64字符串。最重要的是要记住Facebook用户不需要知道这些信息才能登录,因为这是使用Facebook身份验证的重点。这使他们可以在以后根据实际数据添加此信息。

4

现在是在主应用程序页面上添加按钮的好时机,让用户让他们实际登录到应用程序......

<强> _header.html.erb

<% if logged_in? %>
  .
  .
  .
<% else %>
  .
  .
  <li><%= link_to "Sign in with Facebook", "/auth/facebook", id: "sign_in" %></li>
<% end %>

5

所以,现在我们可以向facebook发送请求了。一个问题仍然是我们无法实际处理回调。为此,我们需要做以下事情......

  • get 'auth/:provider/callback' => 'sessions#create_facebook'添加到 routes.rb 文件

  • 使用Facebook提供的方便的Javascript SDK。让我们创建一个使用此SDK的自定义javascript文件...

应用/资产/ Javascript角/ facebook.js.erb

jQuery(function() {
  $('body').prepend('<div id="fb-root"></div>');
  return $.ajax({
    url: window.location.protocol + "//connect.facebook.net/en_US/sdk.js",
    dataType: 'script',
    cache: true
  });
});

window.fbAsyncInit = function() {
  FB.init({
    appId: 'your_facebook_app_id_goes_here',
    version: 'v2.3',
    cookie: true
  });
  $('#sign_in').click(function(e) {
    e.preventDefault();
    return FB.login(function(response) {
      if (response.authResponse) {
        return window.location = '/auth/facebook/callback';
      }
    });
  });
  return $('#sign_out').click(function(e) {
    FB.getLoginStatus(function(response) {
      if (response.authResponse) {
        return FB.logout();
      }
    });
    return true;
  });
};

6

就是这样,除了一个小警告......

problem

如果用户决定去编辑个人资料,他/她会看到我们的虚拟电子邮件显示在中间。这发生了,因为Rails非常聪明,可以自动填充用户的信息。在这种情况下,坦率地说,非常尴尬。但是,好的方面是它很容易解决。只需在表单中将值设置为nil,就像这样......

<%= f.email_field :email, value: nil, class: 'form-control' %>

希望这有助于那些想从头开始构建身份验证系统的人:)

答案 1 :(得分:1)

好的,我试图将omniauth与教程中的自定义授权代码集成。经过一些研究后,我决定删除所有教程授权代码,然后选择Devise。

答案 2 :(得分:0)

Timur的回答很好,但是假设OP已经为OmniAuth配置了一些东西。不幸的是,有些读者可能没有完成配置,并且会收到错误消息。 omn​​iauth的最低配置是设置facebook凭证,使其可用于应用程序,并使用以下命令创建一个名为config / initializers / omniauth.rb的文件:

    Rails.application.config.middleware.use OmniAuth::Builder do
      provider :developer unless Rails.env.production?
      provider :facebook, ENV['FACEBOOK_KEY'], ENV['FACEBOOK_SECRET']
    end

当然,为该功能编写一些测试是明智的。

此问题和Timur的回答都没有涉及用户创建本地登录帐户然后尝试从Facebook登录的情况。如果Facebook电子邮件与其他电子邮件不同,它将由于电子邮件重复或创建新帐户而失败。应该更新User.from_omniauth(auth)函数以处理这种情况。 omn​​iauth上有一个有关如何处理此问题的页面,但是需要Miachal的hartl教程中的大量修改:https://github.com/omniauth/omniauth/wiki/Managing-Multiple-Providers