这篇文章简单的分析一下AuthorenticatedSystem类,我把相关连的代码放在一起,方便分析,首先列出最常用的三个方法
ruby 代码
-
-
- def logged_in?
- current_user != :false
- end
-
-
- def current_user
- @current_user ||= (session[:user] && User.find_by_id(session[:user])) || :false
- end
-
-
- def current_user=(new_user)
- session[:user] = (new_user.nil? || new_user.is_a?(Symbol)) ? nil : new_user.id
- @current_user = new_user
- end
调用logged_in?的时候,会调用current_user方法,如果是第一次调用,会进而调用模型类的find方法,使用id来进行查找,否则直接返回@current_user实例变量
调用current_user=()方法,如果传入参数是nil或者symbol,session[:user]被设置为nil,否则设置为相应模型类的id,同时设置@current_user实例变量
使用@current_user大概是为了减少数据库查询次数
ruby 代码
- def login_required
- username, passwd = get_auth_data
- self.current_user ||= User.authenticate(username, passwd) || :false if username && passwd
- logged_in? && authorized? ? true : access_denied
- end
-
- def authorized?
- true
- end
-
- @@http_auth_headers = %w(X-HTTP_AUTHORIZATION HTTP_AUTHORIZATION Authorization)
-
- def get_auth_data
- auth_key = @@http_auth_headers.detect { |h| request.env.has_key?(h) }
- auth_data = request.env[auth_key].to_s.split unless auth_key.blank?
- return auth_data && auth_data[0] == 'Basic' ? Base64.decode64(auth_data[1]).split(':')[0..1] : [nil, nil]
- end
-
- def access_denied
- respond_to do |accepts|
- accepts.html do
- store_location
- redirect_to :controller => '/account', :action => 'login'
- end
- accepts.xml do
- headers["Status"] = "Unauthorized"
- headers["WWW-Authenticate"] = %(Basic realm="Web Password")
- render :text => "Could't authenticate you", :status => '401 Unauthorized'
- end
- end
- false
- end
-
- def store_location
- session[:return_to] = request.request_uri
- end
login_required是一个filter方法,通过在ApplicationController类中include AuthorenticatedSystem类,然后在相应的模型类中定义一个before_filter :login_required来使用,login_required通过get_auth_data方法从request请求中获得auth_key和auth_data,将auth_data反编码后取出其中的值,此返回值是一个数组,如果request请求中不包含auth_data,则返回[nil,nil]
如果get_auth_data返回一个[nil,nil],或者request包含的用户名密码不正确,会导致session[:user]和@current设置为:false,logged_in?返回false,导致最终调用access_denied方法,此方法会记录用户当前请求的连接地址,放入一个叫session[:return_to]的session中,并且重定向到login方法,session[:return_to]会在login中用到。
ruby 代码
- def self.included(base)
- base.send :helper_method, :current_user, :logged_in?
- end
logged_in?方法被注册为helper方法,从而可以在视图中使用,用来控制视图逻辑。
ruby 代码
-
-
- def login_from_cookie
- return unless cookies[:auth_token] && !logged_in?
- user = User.find_by_remember_token(cookies[:auth_token])
- if user && user.remember_token?
- user.remember_me
- self.current_user = user
- cookies[:auth_token] = { :value => self.current_user.remember_token , :expires => self.current_user.remember_token_expires_at }
- flash[:notice] = "Logged in successfully"
- end
- end
login_from_cookie方法应该置于ApplicationController中并且申明为before_filter,这样可以在整个网站获得remember_me功能
ruby 代码
- def login_from_cookie
- return unless cookies[:auth_token] && !logged_in?
- user = User.find_by_remember_token(cookies[:auth_token])
- if user && user.remember_token?
- user.remember_me
- self.current_user = user
- cookies[:auth_token] = { :value => self.current_user.remember_token , :expires => self.current_user.remember_token_expires_at }
- flash[:notice] = "Logged in successfully"
- end
- end
如果cookies中不包含:auth_token,或者用户已经登录,则直接返回。根据cookies[:auth_token]查找user,调用remember_token?方法判断用户是否已经过期(插件默认过期时间为两个星期)
ruby 代码
- def remember_token?
- remember_token_expires_at && Time.now.utc < remember_token_expires_at
- end