【问题标题】:Editing Users With Devise and Omniauth使用 Devise 和 Omniauth 编辑用户
【发布时间】:2012-11-06 08:06:31
【问题描述】:

我正在通过 Railscast 实施 Devise 和 OmniAuth(以及 Devise documentation)——目前,我有一个网站,访问者可以使用他们的 Facebook 帐户或填写表格进行注册.

不过,当通过 OmniAuth 注册的用户尝试编辑他们的个人资料时,我遇到了麻烦。 Devise 会在用户提交对个人资料的更改时查找用户的当前密码,但使用 facebook 登录的用户不知道他们的密码(它们在用户模型中自动设置):

  def self.find_for_facebook_oauth(auth, signed_in_resource=nil)
    user = User.where(:provider => auth.provider, :uid => auth.uid).first
    unless user
      user = User.create(first_name:auth.extra.raw_info.first_name,
                         last_name:auth.extra.raw_info.last_name,
                           provider:auth.provider,
                           uid:auth.uid,
                           email:auth.info.email,
                           password:Devise.friendly_token[0,20]
                           )
    end
    user
  end

当用户编辑他的信息时,如果他通过 OmniAuth 设置他的帐户,应用程序不应要求密码确认。本教程建议使用方便的 password_required?方法将帮助我实现这个结果。具体来说,将这个方法添加到用户模型意味着它应该只在用户没有通过 OmniAuth 注册时才返回 true(在这种情况下提供者属性将为 nil):

def password_required?
  super && provider.blank?
end

因此,一段代码如下:

<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :method => :put }) do |f| %>
  <%= devise_error_messages! %>
    <%= render :partial => "essential_user_info_inputs", :locals => { :f => f } %>
    <%= render :partial => "inessential_user_info_inputs", :locals => { :f => f } %>
    <% if f.object.password_required? %> 
        <%= render :partial => "password_inputs", :locals => { :f => f } %>
        <%= f.label :current_password %> <i>(we need your current password to confirm your changes)</i><br />
        <%= f.password_field :current_password %>
    <% end %>
  <%= f.submit "Update" %>
<% end %>

理论上只会在需要时显示密码输入。它还表明,Devise 内置了逻辑,即 OmniAuth 用户不需要使用密码来编辑他们的帐户。我不知道这是否属实,但教程有点让它看起来像那样。但是当 OmniAuth 用户尝试编辑他的帐户时,我得到“当前密码不能为空”。非 OmniAuth 用户也是如此(这是有道理的,因为密码字段也不会显示在这些用户的编辑页面上)。

四处寻找确认密码是必需的?当用户通过 OmniAuth 注册和通过站点的常规用户注册时,方法返回 false。即使我将其更改为简单地运行超类方法,它也会返回 false。

对 password_required 方法发生了什么有任何想法?我在任何地方都找不到任何关于它的信息,但我觉得这就是现在的绊脚石。

更新:

这现在可以工作了,但没有使用 Railscast 中概述的方法,它依赖于 requires_password?方法,一个我仍然一无所知的话题。相反,我按照here 的建议实施了here 概述的解决方案。所以我现在只需要密码来使用代码更新非 OmniAuth 帐户:

class Users::RegistrationsController < Devise::RegistrationsController
def update
    @user = User.find(current_user.id)
    email_changed = @user.email != params[:user][:email]
    is_facebook_account = !@user.provider.blank?

    successfully_updated = if !is_facebook_account
      @user.update_with_password(params[:user])
    else
      @user.update_without_password(params[:user])
    end

    if successfully_updated
      # Sign in the user bypassing validation in case his password changed
      sign_in @user, :bypass => true
      redirect_to root_path
    else
      render "edit"
    end
  end
end

【问题讨论】:

标签: ruby-on-rails devise ruby-on-rails-3.2 omniauth


【解决方案1】:

我在某个地方看到过这个。


def update
    params[:user].delete(:current_password)
    params[:user].delete(:password)
    params[:user].delete(:password_confirmation)
    if current_user.update_without_password(params[:user])
      redirect_to somewhere_wicked_path, notice => "You rock"
    else
      render 'edit', :alert => 'you roll'
    end
end

在控制器的更新方法中使用类似的东西。很确定该方法也在设计中。

【讨论】:

  • 不需要那些 params[:user].delete,因为 OP 将这些字段隐藏在表单中,因此它们永远不会被发送。
  • 听起来不错。我会更新我从中窃取的代码,看看你是否正确。代码越少越好。
【解决方案2】:

我在下面的链接中添加了更新,其中包括我对 Devise/OmniAuth 更改用户个人资料/密码问题的解决方案,并收集了一些有用的链接:

stackoverflow - Allowing users to edit accounts without saving passwords in devise

【讨论】:

    【解决方案3】:

    最简单的方法是覆盖 RegistrationsController 中的 update_resource 方法。这是由他们自己的控制器实现中的设计人员建议的:

      # By default we want to require a password checks on update.
      # You can overwrite this method in your own RegistrationsController.
      def update_resource(resource, params)
        resource.update_with_password(params)
      end
    

    所以解决方案是在你自己的控制器中覆盖这个方法,如下所示:

    class Users::RegistrationsController < Devise::RegistrationsController
    
      # Overwrite update_resource to let users to update their user without giving their password
      def update_resource(resource, params)
        if current_user.provider == "facebook"
          params.delete("current_password")
          resource.update_without_password(params)
        else
          resource.update_with_password(params)
        end
      end
    
    end
    

    【讨论】:

    • 简单直接!。完美运行。
    • 这正是我正在寻找的缺失部分。效果很好。
    • 我还在 update_resource 方法上方添加了 protected 并更新了我的路由以使用控制器 devise_for :users, controllers: { omniauth_callbacks: "users/omniauth_callbacks", registrations: 'users/registrations' } 按照 github.com/plataformatec/devise/wiki/…
    • 我认为current_user.provider == "facebook" 应该是resource.provider == "facebook" current_user 并不总是可用的。例如,如果用户被称为current_logincurrent_user_loginresource 方法被解析为当前的@request.env['devise.mapping']。也许值得改变答案
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-19
    • 1970-01-01
    相关资源
    最近更新 更多