使用HTTP基本身份验证和restful_authentication插件退出登录

10

我在一个Rails应用中安装了restful_authentication插件,其中有一个sessions_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个回答

13

在这种情况下,服务器端无法执行“注销”用户的操作。当用户通过基本身份验证登录时,浏览器会存储认证信息,并在每个请求中通过HTTP头部发送认证参数。如果用户使用基本身份验证登录,则必须关闭浏览器窗口才能注销。


1
这有点像是一个hack,但它解决了我的问题。将用户重定向到一个URL,其中嵌入了错误的用户ID。https://dev59.com/BG025IYBdhLWcg3whGax - Snekse
Snekse在其他SO问题中的建议对我也起作用了。 - Joshua Pinter

5

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

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

我知道,派对结束有点晚了,但如果你想注销,你可以呈现401。

因此,注销方法可能如下所示:

def logout
  render :logout, status: 401
end

您的Restful操作可能如下所示:

您的Restful操作可能如下所示:

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基本身份验证


2

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

1

我刚刚更新了authenticated_system中的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

1
这个解决方案的问题在于它只是关闭了HTTP基本身份验证,而这不是我想要做的。另外,如果你想像这样关闭基本身份验证,你应该删除def current_user内部对该方法的调用,而不是重写它。 - Chris Drappier

1
嗯,看起来客户端浏览器只是缓存了HTTP基本认证凭据,并在每次重新发送它们。如果是这样的话,您对此无法控制。您希望受保护的操作需要使用restful_authentication插件的适当before_filter进行保护,应该是这样的。
require_authentication

所以在你的控制器中,你会有以下代码

before_filter :require_authentication

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

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

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接