【问题标题】:Password validation triggers when it should not be triggering?密码验证在不应该触发时触发?
【发布时间】:2014-06-08 06:17:01
【问题描述】:

为用户制定密码重置机制。密码长度验证正在触发,我正在尝试了解原因。

user.rb

class User < ActiveRecord::Base

  has_secure_password

  validates :password, length: { minimum: 6 }
  ...

  def create_password_reset_token
    self.update_attributes!(password_reset_token: SecureRandom.urlsafe_base64,    password_reset_sent_at: Time.zone.now)
  end

  def reset_password(params)
    self.update_attributes!(params)
    self.update_attributes!(password_reset_token: nil, password_reset_sent_at: nil)
  end

end

password_resets_controller.rb

def create
  user = User.find_by_email(params[:email])
  if user
    user.create_password_reset_token
    UserMailer.password_reset_email(user).deliver 
    redirect_to root_url, :notice => "Email sent with password reset instructions!"
  else
    flash[:error] = "A user with that email address could not be found."
    render 'new'
  end
end

def edit
  @user = User.find_by_password_reset_token(params[:id])
  if @user
    render 'edit'
  else
    flash[:error] = "Invalid password reset code."
    redirect_to root_url
  end
end

def update
  @user = User.find_by_password_reset_token(params[:id])
  if @user.password_reset_sent_at < 2.hours.ago
    flash[:error] = "Password reset has expired."
    redirect_to new_password_reset_path
  elsif @user.reset_password(user_params)
    flash[:success] = "Password has been reset."
    redirect_to root_url
  else
    render 'edit'
  end
end

password_resets/new.html.erb

<%= form_tag password_resets_path, :method => :post do %>
  <%= label_tag :email %>
  <%= text_field_tag :email, params[:email] %>

  <%= submit_tag "Reset Password" %>
<% end %>

password_resets/edit.html.erb:

<%= form_for @user, :url => password_reset_path(params[:id]) do |f| %>

  <h1 class="centertext">Reset Password</h1>

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

  <%= f.label :password %>
  <%= f.password_field :password %>

  <%= f.label :password_confirmation, "Confirm password" %>
  <%= f.password_field :password_confirmation %>

  <%= f.submit "Update password" %>

<% end %>

错误是:

Validation failed: Password is too short (minimum is 6 characters)

抛出它的那一行在create_password_reset_token方法里面:

self.update_attributes!(password_reset_token: SecureRandom.urlsafe_base64, password_reset_sent_at: Time.zone.now)

为什么这里会触发验证?我没有对密码本身做任何事情。我只是在用户记录中创建一个令牌和一个时间。

将验证更改为 on: :create 使其不会触发。问题是用户可以将他们的密码重置为少于六个字符。

澄清

要明确,操作顺序是:

  1. 用户单击“我忘记了密码”的链接。
  2. 它们被带到 password_reset_controller/new.html.erb。此表单有一个字段:电子邮件地址。他们输入电子邮件并提交。
  3. 控制器检查该用户是否存在。如果是,它会告诉模型生成一个 password_reset_token。
  4. 然后,控制器命令向用户发送一封电子邮件,其 URL 包含令牌。
  5. 用户单击 URL。如果令牌有效,他们将被带到 edit.html.erb 并输入他们的新电子邮件及其确认。
  6. 控制器调用reset_password方法,实际上是重置用户密码。

目前,验证在第 2 步触发,在他们输入电子邮件并单击提交后。

【问题讨论】:

  • 为什么你的new.html.erb 没有password field?您如何在没有密码字段的情况下重置他的密码? validationpassword field 上被调用。所以验证正在触发,因为没有password field
  • 我在密码重置控制器上有一个编辑方法和一个对应的edit.html.erb。我省略了它们,因为验证在它到达之前就被触发了。
  • 我添加了它们以尽量减少进一步的混淆。
  • 您确定user 给出的password 至少是六个字符吗?

标签: ruby-on-rails validation activerecord ruby-on-rails-4


【解决方案1】:

您的 create_password_reset_token 正在调用 update_attributes,这将触发对用户模型中每个字段的验证,从而触发密码验证,因为它没有当前的一组

你需要

1) 对那些特定字段使用 update_attribute 并且不会触发验证

2) 将一些密码重置字段或枚举添加到您的模型中,并在单击密码重置按钮时将其设置为 true,然后在您的用户模型中执行类似的操作

has_secure_password :validations => false
validates :password, length: {minimum: 6}, unless: -> { user_password_reset? }

3) 使用设计 gem 为您解决这个问题

更新:

试试这个

def create_password_reset_token
  self.update_attribute(:password_reset_token, SecureRandom.urlsafe_base64)        
  self.update_attribute(:password_reset_sent_at, Time.zone.now)
end

【讨论】:

  • 我理解验证触发的原因。我不明白为什么它会失败。用户当前的密码大于六个字符。他们单击链接以重置它并输入他们的电子邮件地址(以便系统可以向他们发送带有重置令牌的电子邮件)。用户的密码绝不会少于六个字符。
  • 所以当他们点击按钮重置密码时,密码不会重置为空?
  • 没错。在用户填写 edit.html.erb 表单之前不会触碰密码。
  • 您能否在更新控制器方法中发布传递给 @user.reset_password 的 user_params,我对如何重置密码感到困惑
  • params.require(:user).permit(:password, :password_confirmation)
【解决方案2】:

我通过在密码验证中添加 Proc 语句解决了这个问题,如下所示:

validates :password, length: { minimum: 6 }, unless: Proc.new { |a| !a.password_reset_token.nil? }

现在验证会在用户创建和密码重置期间运行,但不会在设置密码重置令牌的时间间隔内运行。所有测试都通过了。

【讨论】:

    猜你喜欢
    • 2015-08-03
    • 1970-01-01
    • 2014-07-01
    • 2016-11-27
    • 2017-09-20
    • 1970-01-01
    • 2020-09-25
    • 1970-01-01
    • 2019-06-17
    相关资源
    最近更新 更多