使用http基本身份validation和restful_authentication插件注销

我在rails应用程序中安装了restful_authentication插件,其session_controller具有如下所示的destroy方法:

def destroy self.current_user.forget_me if logged_in? cookies.delete :auth_token reset_session flash[:notice] = "You have been logged out." redirect_back_or_default('/') end 

在应用程序控制器中我有:

 before_filter :login_required 

在sessions_controller我有:

 skip_before_filter :login_required 

我的问题是,当用户使用http基本身份validation进行身份validation时,他/她不会注销。 会话被销毁,但用户可以毫无问题地导航到受限制的页面。 通过插件进行会话身份validation不会发生此问题。 如何使这种方法摆脱基本的认证?

服务器端无法在这种情况下“注销”用户。 当用户通过基本身份validation登录时,浏览器会存储身份validation信息,并在每次请求时通过http标头发送身份validation参数。 如果用户使用基本身份validation登录,他/她将必须关闭他/她的浏览器窗口才能注销。

通过使用会话变量来记住哪个用户已经注销,我找到了一种非常有趣的方法来克服这个问题。 我们的想法是,即使浏览器仍在发送身份validation数据,我们也只是忽略它,因为用户选择注销。 每当向浏览器发送新的登录请求时,所有认证数据都将被删除,因此用户可以随时重新登录。

 class ApplicationController < ActionController::Base # ... before_filter :authenticate protected def authenticate authenticate_with_http_basic do |username, password| @current_user = User.find_by_name_and_crypted_password(username, User.digest(password)) @current_user = nil if @current_user && session[:logged_out] == @current_user.id !@current_user.nil? end end def authenticate! return if @current_user session[:authenticate_uri] = request.request_uri redirect_to('/login') end end 

然后,在事件控制器上我做:

 class EventsController < ApplicationController before_filter :authenticate!, :only => [ :new, :create, :edit, :update ] #... end 

最后我的会话控制器看起来像这样:

 class SessionController < ApplicationController before_filter :authenticate!, :only => [ :create ] def create if session[:authenticate_uri] redirect_to(session[:authenticate_uri]) session[:authenticate_uri] = nil else redirect_to(new_event_path) end end def destroy session[:logged_out] = @current_user.id redirect_to '/' end protected def authenticate! authenticate_or_request_with_http_basic("Rankings") do |username, password| @current_user = User.find_by_name_and_crypted_password(username, User.digest(password)) if @current_user && session[:logged_out] == @current_user.id @current_user = nil session[:logged_out] = nil end !@current_user.nil? end end end 

不要忘记你的路线!

  map.logout 'login', :controller => 'session', :action => 'create' map.logout 'logout', :controller => 'session', :action => 'destroy' 

这仅适用于IE 6 SP1 +:

 javascript:void(document.execCommand('ClearAuthenticationCache', false)); 

http://msdn.microsoft.com/en-us/library/ms536979(VS.85).aspx

请注意,这将清除用户当前登录的所有站点的缓存(在同一IE实例中)。

嗯,听起来客户端浏览器只是缓存HTTP Basic Auth凭证并且每次都重新发送它们。 在这种情况下,你无法控制它。 您希望保护的操作需要使用适当的before_filter来保护restful_authentication插件,该插件应该是

 require_authentication 

所以在你的控制器中你会有

 before_filter :require_authentication 

HTTP身份validation是无状态的 – 也就是说,服务器不跟踪经过身份validation的“会话” – 因此,客户端必须每次都提供它(因此频繁的复选框“存储这些凭据”),因此服务器无法实现清除客户端凭据。 这是规范的一部分。 请参阅维基百科条目

http://en.wikipedia.org/wiki/Basic_access_authentication

具体来说,请看“缺点”部分。

我刚刚将authenticated_sytem中的login_from_basic_auth更新为:

  def login_from_basic_auth false # authenticate_with_http_basic do |login, password| # self.current_user = User.authenticate(login, password) # end end 

解决此问题的一种方法是完全禁用“基本http身份validation”

但是我们需要这样做才能在Ajax操作期间获得良好的用户体验,因此我们仅针对ajax操作启用了此身份validation

 def login_from_basic_auth return false unless request.xhr? authenticate_with_http_basic do |login, password| self.current_user = User.authenticate(login, password) end end 

我知道,派对后一点点,但如果你想退出,你可以渲染401。

注销方法可能如下所示:

 def logout render :logout, status: 401 end 

你的宁静行动可能如下:

 def destroy self.current_user.forget_me if logged_in? cookies.delete :auth_token reset_session redirect_to '/', status: 401, flash: "You have been logged out." end 

和浏览器将再次引发http基本身份validation