1

我想在 Rails 4.0 应用程序中User使用时为 a 自动生成密码has_secure_password(注意:这就是为什么没有attr_accessor)。

我有一个非常简单的User模型:

class User < ActiveRecord::Base
  has_secure_password
  validates :email, presence: true
end

我想知道如何SecureRandom.hex(8)before_save通话中使用创建密码。我尝试添加before_save { self.password = SecureRandom.hex(8) }它的各种版本,但是当我尝试将它保存在 Rails 控制台中时,它说它password是空的。

任何帮助将不胜感激!

4

2 回答 2

0

尝试改用before_validation回调。before_save之后运行validate,因此您的记录将始终无效。

还要考虑检查是否已经设置了密码。否则,您可以在每次更改记录时设置一个新记录。

在此处查找有关ActiveRecord 回调链的更多信息。

于 2013-04-04T22:33:29.233 回答
0

回调是一个有用的解决方案,但在您添加所需的复杂层时会使测试变得困难。

另一种方法是创建一个位于用户“之上”的类。我不会争辩说这是最好的解决方案,但我发现它很有用。请注意,该类继承自User该类:

# user_with_default_password.rb
class UserWithDefaultPassword < User
  def initialize(options = {})
    random_password = SecureRandom.hex(8)

    options[:password] = random_password

    # If creating a user with confirmation requirements
    options[:password_confirmation] = random_password

    # Call User.new(options)
    super(options)
  end
end

用法:

user = UserWithDefaultPassword.new() # => user.password = "ajfladfjkf..."

这样做的好处是您可以测试 User 和 UserWithDefaultPassword 而无需强制执行通常会减慢测试速度的回调。

此外,如果您想简单地使用User.new()而不是UserWithDefaultPassword.new(). 它提供了更大的灵活性和更好的测试。

更新:2014-04-21

此外,我建议将用于创建随机密码等的代码提取到一个单独的类中,您可以快速测试(无需加载 Rails 框架)。下面是我最近为执行其中一些简单任务而开设的课程。

#==================================================
# Generates random/unique strings/tokens/IDs
# See: http://ruby-doc.org/stdlib-2.0.0/libdoc/securerandom/rdoc/SecureRandom.html
# See: http://ruby.railstutorial.org/chapters/sign-in-sign-out#sec-signin_success
#==================================================
class Generator
  require "securerandom"

  # General-purpose encryption using SHA1 instead of bcrypt; faster but LESS SECURE than bcrypt
  # (do not use for extremely sensitive data such as passwords)
  def self.encrypt(value)
    if value.nil? || value.empty?
      ""
    else
      Digest::SHA1.hexdigest(value.to_s)
    end
  end

  # Although a UUID would work as an auto-generated password,
  # it just seems more appropriate to make the password a random
  # string.
  def self.random_password
    SecureRandom.urlsafe_base64(32)
  end

  # Used as a random, unique token
  def self.uuid
    SecureRandom.uuid
  end

  # Returns random number as string for admin 2-step authentication
  def self.verification_code(length)
    value = ""

    if length.nil? || length.zero?
      length = 6
    elsif length > 20
      length = 20
    end

    length.times do |num|
      value += SecureRandom.random_number(10).to_s
    end

    value
  end
end

用法:Generator.random_password # => "abc123..."

最大的好处是您可以在不加载 Rails 框架的情况下测试随机密码的创建,从而节省 3-4 秒的加载时间。这(大部分)通过 Rails 4.1 解决,它使用 Spring 预加载您的应用程序进行测试,但如果您不在 Rails 4.1(或使用 Spring gem)上,这是一个更好的解决方案。

于 2014-04-21T19:58:36.767 回答