原文:has_secure_password with Rails 4.1
我剛剛用Rails 4.1創建了一個新項目,并且試用了has_secure_password
,很酷的功能呢。
但愿你沒有在數據庫里直接存儲明文密碼!為了防治密碼被竊取,數據庫中存儲的始終應該是某種形式的哈希值,而非明文密碼。
有幾個很棒的教程講述如何以安全的方式哈希和存儲密碼。我自己用Ruby實現過幾次。
更復雜一點的解決方案是 devise,一個關于安全和驗證的強大gem。
但是,Rails 3.1 之后你也可以利用has_secure_password來實現。這個Rails內部的機制充分考慮了密碼驗證和加密。它需要model中有一個password_digest
字段,用來存儲加密后的密碼。讓我們來生成一個簡單的model。
rails g model user username:string password_digest:string
這user model中加入這段代碼
has_secure_password
這會為model加上password
和password_confirmation
兩個屬性。這兩個屬性是model的一部分,但是并不存在于數據庫中!因為我們并不需要存儲明文密碼。
來加一些測試:
require 'spec_helper'
describe User do
it "fails because no password" do
User.new({:username => "hans"}).save.should be_false
end
it "fails because passwrod to short" do
User.new({:username => "hans", :password => 'han'}).save.should be_false
end
it "succeeds because password is long enough" do
User.new({:username => "hans", :password => 'hansohanso'}).save.should be_true
end
end
3個簡單的測試用例:
- 不提供密碼,用戶創建失敗
- 密碼過短,用戶創建失敗
- 密碼合法,用戶創建成功
用 RSpec 跑這些用例時,第二個用例會失敗。因為默認Rails不會加入密碼長度的驗證?,F在我們在model中自己加上。
class User < ActiveRecord::Base
has_secure_password
validates :password, :length => { :minimum => 5 }
end
再跑一次測試,這次用例都通過了。檢查一下數據庫會發現users表的password_digest列存儲著經過加密的數值,這正是我們想要的!
現在來做驗證的部分。假設一個新的注冊用戶現在想要登錄。你可以這樣進行驗證:
user = User.find_by_username("USERNAME").authenticate("CLEAR_TEXT_PASSWORD")
如果從HTTP請求中傳來的用戶名和明文密碼都正確,將從數據庫中返回一個有效用戶。否則返回nil
!
has_secure_password
只在對象創建時對密碼進行驗證。之后的操作便不再驗證(例如update)。這個可以接受,因為這意味著你可以從數據庫加載用戶,在不知道密碼的情況下修改和保存用戶。
has_secure_password
還為model提供了password_confirmation
屬性。只有當該屬性為非nil
值時才觸發驗證。如果該屬性非nil
,則其值必須與password
屬性相等。讓我們針對該屬性再加兩個測試用例。
it "fails because password confirmation doesnt match" do
User.new({:username => "hans",
:password => 'hansohanso',
:password_confirmation => 'aa'}).save.should be_false
end
it "succeeds because password & confirmation match" do
User.new({:username => "hans",
:password => 'hansohanso',
:password_confirmation => 'hansohanso'}).save.should be_true
end
為了讓測試通過,在model中再加入一行代碼。
class User < ActiveRecord::Base
has_secure_password
validates :password, :length => { :minimum => 5 }
validates_confirmation_of :password
end
validates_confirmation_of :password
將會檢查密碼的一致性。
Rails不強制使用password_confirmation
,但是你需要的話可以像這么來用。
我確實很喜歡這個功能,因為它節省了很多代碼和開發時間。而且對大部分應用來說夠用了。
譯注
has_secure_password
會自動加入以下驗證規則:
- 在創建時密碼必須存在
- 密碼長度小于等于72字符
- 確認密碼(使用
password_confirmation
屬性)
在實際項目中,通過第三方應用登錄的用戶是不需要密碼的,這時可以通過傳遞validations: false
參數移除驗證規則。
has_secure_password :validations => false
另
- 模塊驗證規則查看
User.validators
- 模塊中某屬性驗證規則查看
User.validators_on(:password)