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

时间:2009-03-18 21:20:27

标签: ruby-on-rails authentication ruby-on-rails-plugins

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

7 个答案:

答案 0 :(得分:13)

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

答案 1 :(得分:4)

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

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'

答案 2 :(得分:2)

这仅适用于IE 6 SP1 +:

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

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

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

答案 3 :(得分:1)

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

require_authentication

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

before_filter :require_authentication

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

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

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

答案 4 :(得分:1)

我刚刚将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

答案 5 :(得分:1)

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

但是我们需要在Ajax操作期间获得良好的用户体验,因此我们仅针对ajax操作启用此身份验证

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

答案 6 :(得分:1)

我知道,派对后一点点,但如果你想退出,你可以渲染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基本身份验证