原文:has_secure_password with Rails 4.1
我剛剛用Rails 4.1創(chuàng)建了一個新項目,并且試用了has_secure_password
,很酷的功能呢。
但愿你沒有在數(shù)據(jù)庫里直接存儲明文密碼拍棕!為了防治密碼被竊取,數(shù)據(jù)庫中存儲的始終應(yīng)該是某種形式的哈希值,而非明文密碼井佑。
有幾個很棒的教程講述如何以安全的方式哈希和存儲密碼。我自己用Ruby實現(xiàn)過幾次眠寿。
更復(fù)雜一點的解決方案是 devise躬翁,一個關(guān)于安全和驗證的強大gem。
但是盯拱,Rails 3.1 之后你也可以利用has_secure_password來實現(xiàn)盒发。這個Rails內(nèi)部的機制充分考慮了密碼驗證和加密。它需要model中有一個password_digest
字段狡逢,用來存儲加密后的密碼迹辐。讓我們來生成一個簡單的model。
rails g model user username:string password_digest:string
這user model中加入這段代碼
has_secure_password
這會為model加上password
和password_confirmation
兩個屬性甚侣。這兩個屬性是model的一部分明吩,但是并不存在于數(shù)據(jù)庫中!因為我們并不需要存儲明文密碼殷费。
來加一些測試:
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個簡單的測試用例:
- 不提供密碼印荔,用戶創(chuàng)建失敗
- 密碼過短低葫,用戶創(chuàng)建失敗
- 密碼合法,用戶創(chuàng)建成功
用 RSpec 跑這些用例時仍律,第二個用例會失敗嘿悬。因為默認Rails不會加入密碼長度的驗證。現(xiàn)在我們在model中自己加上水泉。
class User < ActiveRecord::Base
has_secure_password
validates :password, :length => { :minimum => 5 }
end
再跑一次測試善涨,這次用例都通過了。檢查一下數(shù)據(jù)庫會發(fā)現(xiàn)users表的password_digest列存儲著經(jīng)過加密的數(shù)值草则,這正是我們想要的钢拧!
現(xiàn)在來做驗證的部分。假設(shè)一個新的注冊用戶現(xiàn)在想要登錄炕横。你可以這樣進行驗證:
user = User.find_by_username("USERNAME").authenticate("CLEAR_TEXT_PASSWORD")
如果從HTTP請求中傳來的用戶名和明文密碼都正確实苞,將從數(shù)據(jù)庫中返回一個有效用戶菌瘪。否則返回nil
肃弟!
has_secure_password
只在對象創(chuàng)建時對密碼進行驗證枝秤。之后的操作便不再驗證(例如update)。這個可以接受卿嘲,因為這意味著你可以從數(shù)據(jù)庫加載用戶颂斜,在不知道密碼的情況下修改和保存用戶。
has_secure_password
還為model提供了password_confirmation
屬性拾枣。只有當(dāng)該屬性為非nil
值時才觸發(fā)驗證沃疮。如果該屬性非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
,但是你需要的話可以像這么來用似扔。
我確實很喜歡這個功能吨些,因為它節(jié)省了很多代碼和開發(fā)時間。而且對大部分應(yīng)用來說夠用了炒辉。
譯注
has_secure_password
會自動加入以下驗證規(guī)則:
- 在創(chuàng)建時密碼必須存在
- 密碼長度小于等于72字符
- 確認密碼(使用
password_confirmation
屬性)
在實際項目中豪墅,通過第三方應(yīng)用登錄的用戶是不需要密碼的,這時可以通過傳遞validations: false
參數(shù)移除驗證規(guī)則黔寇。
has_secure_password :validations => false
另
- 模塊驗證規(guī)則查看
User.validators
- 模塊中某屬性驗證規(guī)則查看
User.validators_on(:password)