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

Posted

技术标签:

【中文标题】使用 http 基本身份验证和 restful_authentication 插件注销【英文标题】:Logout with http basic authentication and restful_authentication plugin 【发布时间】:2010-10-14 04:39:58 【问题描述】:

我在 rails 应用程序中安装了 restful_authentication 插件,其中的 sessions_controller 具有这样的销毁方法:

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

在 session_controller 我有:

skip_before_filter :login_required

我的问题是,当用户使用 http 基本身份验证进行身份验证时,他/她没有注销。会话被破坏,但用户可以毫无问题地导航到受限页面。通过插件进行会话身份验证不会出现此问题。我怎样才能让这个方法摆脱基本的身份验证?

【问题讨论】:

【参考方案1】:

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

【讨论】:

这有点小技巧,但它解决了我的问题。将用户重定向到其中嵌入了错误用户 ID 的 URL。 ***.com/questions/5957822/… Snekse 在另一个 SO 问题中的建议也对我有用。【参考方案2】:

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

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'

【讨论】:

【参考方案3】:

这仅适用于 IE 6 SP1+:

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

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

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

【讨论】:

【参考方案4】:

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

require_authentication

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

before_filter :require_authentication

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

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

具体看“缺点”部分。

【讨论】:

【参考方案5】:

我刚刚将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基本身份验证,这不是我想要做的。另外,如果你想像这样关闭基本身份验证,你应该在 def current_user 中删除对这个方法的调用,而不是重写它。【参考方案6】:

解决此问题的一种方法是完全禁用“基本 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

【讨论】:

【参考方案7】:

我知道,派对结束后,但如果你想退出,你可以渲染 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基本认证

【讨论】:

以上是关于使用 http 基本身份验证和 restful_authentication 插件注销的主要内容,如果未能解决你的问题,请参考以下文章

HTTP 基本身份验证和 OAuth 2.0 相同吗?

通过 SSL 和 HTTP 基本身份验证的 REST 服务

在 URL 和加密中传递的 HTTP 基本身份验证凭据

基本 HTTP 身份验证:用户还是应用程序?

.NET WebAPI 和 http 基本身份验证

如何使用 Guzzle 进行 HTTP 基本身份验证?