使用 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 插件注销的主要内容,如果未能解决你的问题,请参考以下文章