这个模块想必大家都会很熟悉,在rails的基础教程里负责password的加密和验证,主要就是给ActiveRecord::Base添加了password=、password_digest=、password_confirmation=、authenticate方法。
# frozen_string_literal: true
module ActiveModel
module SecurePassword
extend ActiveSupport::Concern
MAX_PASSWORD_LENGTH_ALLOWED = 72
class << self
attr_accessor :min_cost # :nodoc:
end
self.min_cost = false
module ClassMethods
def has_secure_password(attribute = :password, validations: true)
begin
require "bcrypt"
rescue LoadError
$stderr.puts "You don't have bcrypt installed in your application. Please add it to your Gemfile and run bundle install"
raise
end
include InstanceMethodsOnActivation.new(attribute)
if validations
include ActiveModel::Validations
validate do |record|
record.errors.add(attribute, :blank) unless record.send("#{attribute}_digest").present?
end
validates_length_of attribute, maximum: ActiveModel::SecurePassword::MAX_PASSWORD_LENGTH_ALLOWED
validates_confirmation_of attribute, allow_blank: true
end
end
end
class InstanceMethodsOnActivation < Module
def initialize(attribute)
attr_reader attribute
define_method("#{attribute}=") do |unencrypted_password|
if unencrypted_password.nil?
self.send("#{attribute}_digest=", nil)
elsif !unencrypted_password.empty?
instance_variable_set("@#{attribute}", unencrypted_password)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : BCrypt::Engine.cost
self.send("#{attribute}_digest=", BCrypt::Password.create(unencrypted_password, cost: cost))
end
end
define_method("#{attribute}_confirmation=") do |unencrypted_password|
instance_variable_set("@#{attribute}_confirmation", unencrypted_password)
end
define_method("authenticate_#{attribute}") do |unencrypted_password|
attribute_digest = send("#{attribute}_digest")
BCrypt::Password.new(attribute_digest).is_password?(unencrypted_password) && self
end
alias_method :authenticate, :authenticate_password if attribute == :password
end
end
end
end