【问题标题】:Why am I getting "Password can't be blank" during user update when not updating the password (Rails 4 with 'has_secure_password')?为什么在不更新密码(Rails 4 with 'has_secure_password')时用户更新期间出现“密码不能为空”?
【发布时间】:2015-03-15 10:51:48
【问题描述】:

在我的 Rails 4.0.9 应用程序中,使用 BCrypt 3.1.7 和 Clearance 1.4.2 进行身份验证,更新用户信息时返回错误。尽管没有输入新密码或密码确认,但我收到错误“密码不能为空”。

我在 SO 和谷歌上搜索过,但我发现没有一个解决方案适合我。大多数是针对早期版本的 Rails,现在可能已经过时了,而其他的并不真正适合我的确切情况(可能是因为我只是在做一些愚蠢的事情)。

我可以从编辑表单中删除密码和密码确认字段,但我确实希望用户能够更新他们的密码,同时更新他们的姓名或电子邮件地址,如果他们愿意的话。

'has_secure_password' 应该只在创建时验证,但似乎也在更新时验证。为了解决这个问题,我禁用了我的用户模型对密码和密码确认的验证,并简单地使用“has_secure_password”来验证这些参数。你能明白为什么我会收到验证错误吗? Clearance gem 是否覆盖了 has_secure_passwords 的默认行为,或以任何方式影响验证?

用户模型

class User < ActiveRecord::Base
include Clearance::User
belongs_to :studio
has_one :public_user

before_save { self.email = email.downcase }
before_create :create_remember_token
after_create :make_public_user

validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i
validates :email, presence: true,
                format:     { with: VALID_EMAIL_REGEX },
                uniqueness: { case_sensitive: false }
has_secure_password
#   validates :password, length: { minimum: 6, on: :create }
#   validates :password_confirmation, presence: { on: :create }
...

用户控制器

...
def edit
end

def update
  if @user.update_attributes(user_params)
    flash[:success] = "Profile updated"
    redirect_to @user
  else
    render 'edit'
  end
end

private


def user_params
  params.require(:user).permit(:name, :email, :password, :password_confirmation, :password_digest)
end
...

编辑表单(实际上是“_fields.html.erb”部分)

<%= render 'shared/error_messages', object: f.object %>

<div class="control-group">
        <%= f.label :name, class: "control-label" %>
<div class="controls">
        <%= f.text_field :name %>
    </div>
</div>

<div class="control-group">
    <%= f.label :email, class: "control-label" %>
<div class="controls">
        <%= f.text_field :email %>
    </div>
</div>

<div class="control-group">
    <%= f.label :password, class: "control-label" %>
<div class="controls">
        <%= f.password_field :password, placeholder: 'Enter new password if changing' %>
    </div>
</div>

<div class="control-group">
    <%= f.label :password_confirmation, "Confirmation", class: "control-label" %>
    <div class="controls">
        <%= f.password_field :password_confirmation, placeholder: 'Confirm your new password if changing' %>
    </div>
</div>

传递给控制器​​的参数

Parameters: {"utf8"=>"✓", "authenticity_token"=>"[FILTERED]", "user"=>{"name"=>"Ransom Kshlerin III", "email"=>"example-1@samplestudio.com", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}, "commit"=>"Save changes", "id"=>"4"}

编辑:

当我禁用“has_secure_password”时,我仍然收到验证错误,这让我相信 Clearance 的验证正在返回错误。我认为我需要覆盖 Clearance 的密码验证,但仅限于更新用户时。我该怎么办?

编辑 2:

我将采取完全不同的方法。我将提供一个用于重置密码的链接,而不是允许直接在此表单上重置密码,该链接将向用户发送一封电子邮件,其中包含重置密码的链接。

【问题讨论】:

    标签: validation ruby-on-rails-4


    【解决方案1】:

    Clearance 提供了一条清晰的 (^_^) 路径来规避密码要求:

    # Always false. Override this method in your user model to allow for other
    # forms of user authentication (username, Facebook, etc).
    #
    # @return [false]
    def password_optional?
      false
    end
    

    例如,在您的用户模型中,您可以这样做:

    # app/models/user.rb
    
    class User < ApplicationRecord
      include Clearance::User
    
      def password_optional?
        true
      end
    end
    

    Source

    【讨论】:

      【解决方案2】:

      has_secure_password 和 Clearance gem 都验证密码和密码确认,我还不够熟练,无法覆盖 Clearance 的验证(在我的头上)。所以我采取了不同的方法。密码和密码确认不会包含在此编辑表单中。

      我将提供一个用于重置密码的链接,而不是允许直接在此表单上重置密码,该链接将向用户发送一封电子邮件,其中包含重置密码的链接。我想这里的额外好处是,如果用户设法以其他用户身份登录,他们将无法重置该用户的密码,除非他们也入侵了其他用户的电子邮件帐户。

      【讨论】:

        【解决方案3】:

        在传递给控制器​​的参数中有"password"=&gt;"", "password_confirmation"=&gt;"" 存在(不用担心[FILTERED],这只意味着Rails 不会将参数打印到日志中)。

        当调用params.require(:user).permit(:password, :password_confirmation) 时包括{ password: "", password_confirmation: "", # plus the other keys }

        当将此哈希传递给@user.update_attributes({password: '', password_confirmation: ''}) 时,密码的存在验证将启动并防止设置空密码。

        不尝试设置空密码的一个选项可能是从 user_params 哈希中删除密钥,除非它们的值被认为存在。

        def user_params
          user_params = params.require(:user).permit(:name, :email, :password, :password_confirmation) # don't permit `password_digest` here, that database column should not be allowed to be set by the user!
        
          # Remove the password and password confirmation keys for empty values
          user_params.delete(:password) unless user_params[:password].present?
          user_params.delete(:password_confirmation) unless user_params[:password_confirmation].present?
        
          user_params
        end
        

        【讨论】:

        • 当“has_secure_password”在用户模型中被注释掉时,它的验证程序没有运行,对吗?我假设是这样,这意味着 Clearance gem 的验证例程正在返回错误。我相信我需要防止“密码”和“密码确认”在它们留空时被传递给控制器​​,或者在更新用户记录时覆盖 Clearance 的验证。
        • 另外,我尝试按照您的建议从 user_params 中删除这些键,并得到“堆栈级别太深”错误。今天是星期五,外面阳光明媚,我厌倦了今天看这个虫子。我会在星期一回来。 :)
        猜你喜欢
        • 2017-12-07
        • 2015-02-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-04-10
        • 1970-01-01
        • 1970-01-01
        • 2011-01-23
        相关资源
        最近更新 更多