幾乎每一個web應(yīng)用都需要表格登錄座柱。在Ruby中,最流行的選擇就是在Rails應(yīng)用中實用Devise模塊输吏。
Devise提供了許多盒子之外的功能权旷,但這也同樣是令人沮喪的地方。它有很多的“魔法”贯溅,并且和Rails的很多功能高度耦合拄氯。但是如果你的需求和這個gem并不是很切合躲查,舉例說,匿名用戶-你將需要翻閱整個文檔并且閱讀源碼译柏,來查詢?nèi)绾螌崿F(xiàn)它踩晶。這一點也不可愛逃延,并且看上去一點也不值。如果未來的一段時間內(nèi)有驗證需求的改變,這個改變對你來說看起來容易實現(xiàn)還是一場噩夢淘衙?
驗證事實上是非常簡單的!如果你的需求足夠簡單债蓝,你將可以在100行代碼以內(nèi)實現(xiàn)你的驗證功能耗美。在這片文章中,我將給你展示一個小而美的包含登錄盟劫,登出和用戶創(chuàng)建的小的web應(yīng)用夜牡。
所有的源碼都可以在這里找到:
simple_authentication.rb
依賴###
唯一的依賴就是用于創(chuàng)建密碼的BCrypt gem。我們可以直接使用它侣签,在Rails和Devise中都已經(jīng)默認(rèn)包含了塘装。
在本例中,我們將使用Sinatra影所,由于BCrypt 解藕起來非常容易蹦肴,因此代碼可以輕松地翻譯成Rails,這跟Devise很不一樣猴娩。
Gemfile:
source 'https://rubygems.org'
gem 'sinatra', '~>1.4'
gem 'bcrypt', '~>3.1'
BCrypt實現(xiàn)哈希密碼###
需要大約兩行代碼來實現(xiàn)這個需求:
def hash_password(password)
BCrypt::Password.create(password).to_s
end
def test_password(password, hash)
BCrypt::Password.new(hash) == password
end
hash_password函數(shù)生成了一個普通文本密碼阴幌,由單向加密算法加密。要想基于哈希來破解密碼卷中,是一件很困難的矛双、幾乎不可能實現(xiàn)的事情。我們從不存儲密碼蟆豫,只有哈希议忽。所以,當(dāng)有黑客進(jìn)入數(shù)據(jù)庫的事情發(fā)生十减,我們的密碼依然是手保護(hù)的栈幸。
test_password函數(shù)用于檢測當(dāng)給定的密碼是否和之前函數(shù)給出的hash一致。由于我們不在擁有原始密碼帮辟,所以這次通過給定密碼的hash和之前的hash相比較速址。
這是基本的密碼安全,而這所有的工作都交由bcrypt gem來實現(xiàn)织阅。
User Model###
為了讓本例看起來足夠簡單壳繁,User模型只是一個Struct,而“數(shù)據(jù)庫”就是一個數(shù)組。
User = Struct.new(:id, :username, :password_hash)
USERS = [
User.new(1,'Bob',hash_password('The building'))
User.new(2, 'Sally', hash_password('go round'))
]
每個用戶都只有三個基本屬性:id闹炉,username蒿赢,hash。如果這是在Rails當(dāng)中渣触,你可以創(chuàng)建一個只有這三個屬性的模型羡棵。
Signing In###
下列代碼運行于用戶提交登錄表格:
post '/sign_in' do
user = USERS.find{ |u| u.username == params[:username]}
if user && test_password(params[:password],user.password_hash)
session.clear
session[:user_id] = user.id
redirect '/'
else
@error = 'Username or password was incorrect'
erb :sign_in
end
end
安全須知:一旦成功登錄,我們在存儲用戶名之前情調(diào)session嗅钻,這是一種阻止Session Fixation Attacks的安全策略
訪問當(dāng)前用戶###
一旦用戶登入皂冰,我們需要知道未來誰來在發(fā)送請求。下述代碼可以返回當(dāng)前用戶:
def current_user
if session[:user_id]
USERS.find { |u| u.id == session[:user_id] }
else
nil
end
end
當(dāng)我們在登入狀態(tài)時养篓,檢查session是否包含user id秃流,當(dāng)user id存在時,根據(jù)用戶id找到user柳弄。如果是在Rails當(dāng)中舶胀,又可以用User.find(session[:user_id]).
在主頁中,我們可以使用current_user:
<p>Hello, <%= current_user.username %></p>
Signing Out###
如下:
post '/sign_out' do
session.clear
redirect '/sign_in'
end
由于用戶id存儲在session中碧注,我們只需要清除session來sign out嚣伐。簡單而又優(yōu)美!
創(chuàng)建一個新用戶###
代碼如下:
post '/create_user' do
USERS << User.new(
USERS.size + 1,
params[:username],
hash_password(params[:password])
)
redirect '/'
end
在這個小小的實例中萍丐,我們創(chuàng)建了一個User對象轩端,并將它添加到user數(shù)組中。
結(jié)束語###
擁有一個基本驗證的工作實例一點也不難逝变,不是嗎基茵?