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