Login Engine是非常好用的一個(gè)登錄engine,不過也有個(gè)缺點(diǎn),它把用戶信息緩存在session里。如果用戶每次修改完自己的資料,都把session更新的話,自然是不會(huì)有什么數(shù)據(jù)不同步的問題。不過試想這樣一種情況:
1、用戶A登錄;用戶A的信息將保存在session[:user]里。
2、管理員操作用戶A,修改用戶A的資料并保存。
3、用戶A刷新頁面。
如果顯示用戶資料是從session[:user]讀取的話,顯然用戶A看到的是老的資料。
正確的做法是管理員修改用戶資料以后,把用戶session里的內(nèi)容也更新,當(dāng)然這個(gè)實(shí)施起來有些困難,目前看來無法由用戶ID獲得對(duì)應(yīng)的session。
有朋友說session里不應(yīng)該緩存用戶信息,而應(yīng)只保存用戶ID。這是正確的,這樣可以解決上面的問題,不過帶來的問題是每次都要從數(shù)據(jù)庫查詢。
如果每次刷新頁面都從數(shù)據(jù)庫重新讀取用戶信息,對(duì)性能影響是很大的。試想一下用戶正在瀏覽一個(gè)論壇的帖子列表,這個(gè)頁面可能所有用戶看起來都是一樣的,唯一不一樣的地方是上面用戶信息的顯示。由于大部分內(nèi)容都一樣,可以使用緩存加快瀏覽速度。不過卻由于session里只保存了用戶ID,不得不讀取數(shù)據(jù)庫來獲得用戶信息,這樣就把速度又拖慢了。
所以應(yīng)該把用戶信息緩存起來,但要保證它能及時(shí)更新。方法自己做一個(gè)緩存管理器,能根據(jù)用戶ID得到用戶信息,也能隨時(shí)更新它。
學(xué)著ActionController::Caching做了一個(gè)UserManager,它可以根據(jù)線程配置來自動(dòng)開關(guān)互斥器:
(/vender/plugins/login_engine/lib/login_engine/user_management.rb)
module?UserManagement?
#
:nodoc:
??class?UnthreadedUserManager?
#
:nodoc:
????def?initialize?
#
:nodoc:
??????
@users
?
=
?{}
????end
????
????def?get(user_id)
??????
@users
[user_id]
????end
????
????def?set(user_id
,
?user)
??????
@users
[user_id]?
=
?user
????end
??end
??
??module?ThreadSafety?
#
:nodoc:
????def?get(user_id)?
#
:nodoc:
??????
@mutex
.
synchronize?{?super?}
????end
????def?set(user_id
,
?user)?
#
:nodoc:
??????
@mutex
.
synchronize?{?super?}
????end
??end
??
??class?UserManager?
<
?UnthreadedUserManager
????def?initialize
??????super
??????
if
?ActionController
::
Base
.
allow_concurrency
????????
@mutex
?
=
?Mutex
.
new
????????UserManager
.
send
(
:
include
,
?ThreadSafety)
??????end
????end
??end
??
??@
@user_manager
?
=
?UserManagement
::
UserManager
.
new
??
??def?set_current_user(user)
????
return
?session[
:
user_id]?
=
?nil?
if
?user
.
nil
?
????session[
:
user_id]?
=
?user
.
id
????cache_user(user)
??end
??
??def?current_user
????get_user(session[
:
user_id])
??end
??
??def?cache_user(user)
????
return
?
if
?user
.
nil
?
????@
@user_manager
.
set(user
.
id
,
?user)
??end
??
??def?get_user(user_id)
????@
@user_manager
.
get(user_id)
??end
end??
修改(/verdor/plugins/login_engine/lib/login_engine.rb):
#
.
require?'login_engine/user_management'
module?LoginEngine
??include?UserManagement
??#
.
end 加入上面加粗的2行。
修改(/verdor/plugins/login_engine/lib/login_engine/authenticated_system.rb),把session[:user]替換為session[:user_id]。
修改(/verdor/plugins/login_engine/app/controllers/user_controller.rb):
??def?login
????return?if?generate_blank
????@user?=?User.new(params[:user])
????if?user?=?User.authenticate(params[:user][:login],?params[:user][:password])
??????user.logged_in_at?=?Time.now
??????user.save
??????set_current_user(user)
??????flash[:notice]?=?"Login?successful"
??????redirect_to_stored_or_default?:action?=>?'home'
????else
??????@login?=?params[:user][:login]
??????flash.now[:warning]?=?'Login?unsuccessful'
????end
??end
??def?logout
????set_current_user(nil)
????redirect_to?:action?=>?'login'
??end
??def?get_user_to_act_on
????@user?=?current_user
??end
簡(jiǎn)單測(cè)試:
require?'login_engine'
class?ApplicationController?<?ActionController::Base
??include?LoginEngine
??
??helper?:user
??model?:user
????
??before_filter?:login_required
end
class?ShowController?<?ApplicationController
??def?show
????render_text?"User?name:?#{current_user.first_name}"
??end
end
class?AdminController?<?ApplicationController
??def?edit
????user?=?User.find(params[:id])
????user.update_attributes(:first_name?=>?params[:name])
????cache_user(user)
??? render_text "User name: #{user.first_name}"
??end
end
一個(gè)簡(jiǎn)單的模擬:
1、用戶A從IE登錄,訪問/show/show,將顯示用戶的名字。
2、管理員從FF登錄,訪問/show/show,將顯示管理員名字。
3、管理員訪問/show/show/2?name=hello,其中2是用戶A的ID。這將把用戶A的名字修改為hello。
4、用戶A刷新頁面,可以看到顯示的用戶名字已經(jīng)發(fā)生變化。
以上過程說這個(gè)修改已經(jīng)達(dá)到目的。實(shí)現(xiàn)這個(gè)功能并不難,主要是為了保留Login Engine原有的功能不變。
修改后的代碼:
www.shnenglu.com/Files/cpunion/login_engine.rar