Rails自带用户验证has_secure_password的使用与源码分析

来源:互联网 发布:2010nba总决赛数据 编辑:程序博客网 时间:2024/05/22 01:36

Rails自带的用户密码验证是has_secure_password方法,它使用简单,源码也容易理解。本文将介绍如何使用has_secure_password以及其源码的分析。

1. has_secure_password的使用

   has_secure_password的使用非常简单,只需要如下两步: 

  • 为用户模型添加password_digest一列;
  • 在用户模型文件中添加has_secure_password一行。

   这两步完成之后就已经自动添加了用户密码验证的各种方法了,见如下代码:

# Schema: User(name:string, password_digest:string)class User < ActiveRecord::Base  has_secure_passwordenduser = User.new(name: 'david', password: '', password_confirmation: 'nomatch')user.save                                                       # => false, password requireduser.password = 'mUc3m00RsqyRe'user.save                                                       # => false, confirmation doesn't matchuser.password_confirmation = 'mUc3m00RsqyRe'user.save                                                       # => trueuser.authenticate('notright')                                   # => falseuser.authenticate('mUc3m00RsqyRe')                              # => userUser.find_by(name: 'david').try(:authenticate, 'notright')      # => falseUser.find_by(name: 'david').try(:authenticate, 'mUc3m00RsqyRe') # => user

    has_secure_password方法会为User model添加password和password_confirmation两个只读属性,以及一个authenticate方法,用来验证密码的正确性。

2. has_secure_password的源码分析

   has_secure_password的源码非常清晰,很容易就能理解其实现机制。下面是has_secure_password的源码,我做了些简化,保留了核心部分:
  def has_secure_password(options = {})        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        include ActiveModel::Validations        validate do |record|          record.errors.add(:password, :blank) unless record.password_digest.present?        end        validates_length_of :password, maximum: ActiveModel::SecurePassword::MAX_PASSWORD_LENGTH_ALLOWED        validates_confirmation_of :password, if: ->{ password.present? }  end
  这段代码分为三个部分,首先是引入bcrypt依赖包,这个包提供了加密工具,然后引入了InstanceMethodsOnActivation模块,这部分是主要功能的具体实现部分,最后添加了一些验证,在这部分可以看到password_digest这个column必须要存在,否则会出现异常。下面主要来分析InstanceMethodsOnActivation这个模块。

2.1 InstanceMethodsOnActivation

    这个模块的代码如下:
module InstanceMethodsOnActivation  def authenticate(unencrypted_password)    BCrypt::Password.new(password_digest) == unencrypted_password && self  end  attr_reader :password  def password=(unencrypted_password)    if unencrypted_password.nil?      self.password_digest = nil    elsif unencrypted_password.present?      @password = unencrypted_password      cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : BCrypt::Engine.cost      self.password_digest = BCrypt::Password.create(unencrypted_password, cost: cost)    end  end  def password_confirmation=(unencrypted_password)    @password_confirmation = unencrypted_password  endend
   这部分代码是验证的实现细节:

  • 它定义了一个authenticate方法用于验证密码(注意==优先级高于&&),相等则返回self,否则返回false;
  • 声明了只读属性password并实现了其写方法,写入的同时会生成新的password_digest;
  • 实现了password_confirmation的写方法。

      以上就是has_secure_password的源码的实现,其实很简单,完全可以自己手写,但是rails提供方法无疑简化了开发的工作。





1 0
原创粉丝点击