使用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