Blog信息 |
blog名称: 日志总数:1304 评论数量:2242 留言数量:5 访问次数:7578308 建立时间:2006年5月29日 |

| |
[Ruby on Rails]Login Engine补丁 软件技术
lhwork 发表于 2007/2/8 10:11:29 |
Login Engine是非常好用的一个登录engine,不过也有个缺点,它把用户信息缓存在session里。如果用户每次修改完自己的资料,都把session更新的话,自然是不会有什么数据不同步的问题。不过试想这样一种情况:1、用户A登录;用户A的信息将保存在session[:user]里。2、管理员操作用户A,修改用户A的资料并保存。3、用户A刷新页面。
如果显示用户资料是从session[:user]读取的话,显然用户A看到的是老的资料。正确的做法是管理员修改用户资料以后,把用户session里的内容也更新,当然这个实施起来有些困难,目前看来无法由用户ID获得对应的session。有朋友说session里不应该缓存用户信息,而应只保存用户ID。这是正确的,这样可以解决上面的问题,不过带来的问题是每次都要从数据库查询。如果每次刷新页面都从数据库重新读取用户信息,对性能影响是很大的。试想一下用户正在浏览一个论坛的帖子列表,这个页面可能所有用户看起来都是一样的,唯一不一样的地方是上面用户信息的显示。由于大部分内容都一样,可以使用缓存加快浏览速度。不过却由于session里只保存了用户ID,不得不读取数据库来获得用户信息,这样就把速度又拖慢了。所以应该把用户信息缓存起来,但要保证它能及时更新。方法自己做一个缓存管理器,能根据用户ID得到用户信息,也能随时更新它。学着ActionController::Caching做了一个UserManager,它可以根据线程配置来自动开关互斥器:(/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) endend 修改(/verdor/plugins/login_engine/lib/login_engine.rb):
#500)this.width=500'>500)this.width=500'>.require 'login_engine/user_management'module LoginEngine include UserManagement #500)this.width=500'>.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简单测试:
require 'login_engine'class ApplicationController < ActionController::Base include LoginEngine helper :user model :user before_filter :login_requiredend
class ShowController < ApplicationController def show render_text "User name: #{current_user.first_name}" endend
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}" endend一个简单的模拟:1、用户A从IE登录,访问/show/show,将显示用户的名字。2、管理员从FF登录,访问/show/show,将显示管理员名字。3、管理员访问/show/show/2?name=hello,其中2是用户A的ID。这将把用户A的名字修改为hello。4、用户A刷新页面,可以看到显示的用户名字已经发生变化。以上过程说这个修改已经达到目的。实现这个功能并不难,主要是为了保留Login Engine原有的功能不变。修改后的代码:www.cppblog.com/Files/cpunion/login_engine.rar |
|
|