注销 HTTP 身份验证保护文件夹的正确方法是什么?
有一些解决方法可以实现这一点,但它们具有潜在的危险,因为它们可能有问题或在某些情况/浏览器中不起作用。这就是为什么我正在寻找正确和干净的解决方案。
注销 HTTP 身份验证保护文件夹的正确方法是什么?
有一些解决方法可以实现这一点,但它们具有潜在的危险,因为它们可能有问题或在某些情况/浏览器中不起作用。这就是为什么我正在寻找正确和干净的解决方案。
亩。不存在正确的方法,甚至不存在跨浏览器一致的方法。
这是来自HTTP 规范(第 15.6 节)的问题:
现有的 HTTP 客户端和用户代理通常会无限期地保留身份验证信息。HTTP/1.1。没有为服务器提供一种方法来指导客户端丢弃这些缓存的凭据。
另一方面,第10.4.2节说:
如果请求已包含授权凭证,则 401 响应表示已拒绝对这些凭证的授权。如果 401 响应包含与先前响应相同的质询,并且用户代理已经尝试了至少一次身份验证,则应该向用户呈现响应中给出的实体,因为该实体可能包含相关的诊断信息。
换句话说,您可能能够再次显示登录框(正如@Karsten所说),但浏览器不必满足您的请求- 所以不要过于依赖这个(错误)功能。
在 Safari 中运行良好的方法。也适用于 Firefox 和 Opera,但有警告。
Location: http://logout@yourserver.example.com/
这告诉浏览器使用新用户名打开 URL,覆盖以前的用户名。
简单的答案是您无法可靠地退出 http 身份验证。
长答案:
Http-auth(与 HTTP 规范的其余部分一样)意味着是无状态的。因此,“登录”或“注销”并不是一个真正有意义的概念。更好的查看方法是询问每个 HTTP 请求(记住页面加载通常是多个请求),“你可以做你请求的事情吗?”。服务器将每个请求视为新请求,并且与任何先前的请求无关。
浏览器选择记住您在第一个 401 中告诉它们的凭据,并在没有用户对后续请求的明确许可的情况下重新发送它们。这是为用户提供他们期望的“登录/注销”模型的尝试,但这纯粹是一个杂物。模拟这种状态持久性的是浏览器。Web 服务器完全不知道它。
所以“注销”,在 http-auth 的上下文中纯粹是浏览器提供的模拟,因此超出了服务器的权限。
是的,有杂物。但是它们破坏了 RESTful-ness(如果这对你有价值的话)并且它们是不可靠的。
如果您绝对需要登录/注销模型来进行站点身份验证,那么最好的选择是跟踪 cookie,以某种方式(mysql、sqlite、flatfile 等)将状态持久性存储在服务器上。这将需要评估所有请求,例如使用 PHP。
解决方法
您可以使用 Javascript 执行此操作:
<html><head>
<script type="text/javascript">
function logout() {
var xmlhttp;
if (window.XMLHttpRequest) {
xmlhttp = new XMLHttpRequest();
}
// code for IE
else if (window.ActiveXObject) {
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
if (window.ActiveXObject) {
// IE clear HTTP Authentication
document.execCommand("ClearAuthenticationCache");
window.location.href='/where/to/redirect';
} else {
xmlhttp.open("GET", '/path/that/will/return/200/OK', true, "logout", "logout");
xmlhttp.send("");
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4) {window.location.href='/where/to/redirect';}
}
}
return false;
}
</script>
</head>
<body>
<a href="#" onclick="logout();">Log out</a>
</body>
</html>
上面所做的是:
对于 IE - 只需清除身份验证缓存并重定向到某处
对于其他浏览器- 在后台发送带有“注销”登录名和密码的 XMLHttpRequest。我们需要将它发送到某个路径,该路径将对该请求返回 200 OK(即它不应该要求 HTTP 身份验证)。
替换'/where/to/redirect'
为注销后重定向到的某个路径,并替换'/path/that/will/return/200/OK'
为您站点上将返回 200 OK 的某个路径。
解决方法(不是一个干净、漂亮(甚至工作!见评论)的解决方案):
一次禁用他的凭据。
您可以通过发送适当的标头(如果未登录)将 HTTP 身份验证逻辑移至 PHP:
Header('WWW-Authenticate: Basic realm="protected area"');
Header('HTTP/1.0 401 Unauthorized');
并解析输入:
$_SERVER['PHP_AUTH_USER'] // httpauth-user
$_SERVER['PHP_AUTH_PW'] // httpauth-password
因此,一次禁用他的凭据应该是微不足道的。
我对这个问题的解决方案如下。您可以在此页面的第二个示例中http_digest_parse
找到该函数:http: //php.net/manual/en/features.http-auth.php。$realm
$users
session_start();
function LogOut() {
session_destroy();
session_unset($_SESSION['session_id']);
session_unset($_SESSION['logged']);
header("Location: /", TRUE, 301);
}
function Login(){
global $realm;
if (empty($_SESSION['session_id'])) {
session_regenerate_id();
$_SESSION['session_id'] = session_id();
}
if (!IsAuthenticated()) {
header('HTTP/1.1 401 Unauthorized');
header('WWW-Authenticate: Digest realm="'.$realm.
'",qop="auth",nonce="'.$_SESSION['session_id'].'",opaque="'.md5($realm).'"');
$_SESSION['logged'] = False;
die('Access denied.');
}
$_SESSION['logged'] = True;
}
function IsAuthenticated(){
global $realm;
global $users;
if (empty($_SERVER['PHP_AUTH_DIGEST']))
return False;
// check PHP_AUTH_DIGEST
if (!($data = http_digest_parse($_SERVER['PHP_AUTH_DIGEST'])) ||
!isset($users[$data['username']]))
return False;// invalid username
$A1 = md5($data['username'] . ':' . $realm . ':' . $users[$data['username']]);
$A2 = md5($_SERVER['REQUEST_METHOD'].':'.$data['uri']);
// Give session id instead of data['nonce']
$valid_response = md5($A1.':'.$_SESSION['session_id'].':'.$data['nc'].':'.$data['cnonce'].':'.$data['qop'].':'.$A2);
if ($data['response'] != $valid_response)
return False;
return True;
}
假设我有一个名为“密码保护”的 HTTP 基本身份验证领域,并且 Bob 已登录。要注销,我发出 2 个 AJAX 请求:
WWW-Authenticate: Basic realm="Password protected"
此时浏览器忘记了 Bob 的凭据。
通常,一旦浏览器向用户询问凭据并将其提供给特定网站,它将继续这样做而无需进一步提示。与您可以在客户端清除 cookie 的各种方法不同,我不知道有一种类似的方法可以要求浏览器忘记其提供的身份验证凭据。
我发现清除PHP_AUTH_DIGEST
or PHP_AUTH_USER
ANDPHP_AUTH_PW
凭据的唯一有效方法是调用 header HTTP/1.1 401 Unauthorized
。
function clear_admin_access(){
header('HTTP/1.1 401 Unauthorized');
die('Admin access turned off');
}
Trac - 默认情况下 - 也使用 HTTP 身份验证。注销不起作用且无法修复:
- 这是 HTTP 身份验证方案本身的问题,我们无法在 Trac 中正确修复它。
- 目前没有适用于所有主要浏览器的解决方法(JavaScript 或其他)。
来自: http ://trac.edgewall.org/ticket/791#comment:103
看起来这个问题没有有效的答案,这个问题已经在七年前报告过了,这很有意义:HTTP 是无状态的。请求是否使用身份验证凭据完成。但这是客户端发送请求的问题,而不是接收请求的服务器。服务器只能说明请求 URI 是否需要授权。
我需要重置 .htaccess 授权,所以我使用了这个:
<?php
if (!isset($_SERVER['PHP_AUTH_USER'])) {
header('WWW-Authenticate: Basic realm="My Realm"');
header('HTTP/1.0 401 Unauthorized');
echo 'Text to send if user hits Cancel button';
exit;
}
?>
在这里找到它:http: //php.net/manual/en/features.http-auth.php
去搞清楚。
该页面上有许多解决方案,它甚至在底部注明:Lynx,不像其他浏览器那样清除身份验证;)
我在我安装的浏览器上对其进行了测试,一旦关闭,每个浏览器似乎都需要重新进入时重新授权。
这可能不是正在寻找的解决方案,但我是这样解决的。我有 2 个用于注销过程的脚本。
注销.php
<?php
header("Location: http://.@domain.com/log.php");
?>
日志文件
<?php
header("location: https://google.com");
?>
这样我就不会收到警告并且我的会话被终止
AFAIK,在使用 htaccess(即基于 HTTP)身份验证时,没有干净的方法来实现“注销”功能。
这是因为这种身份验证使用 HTTP 错误代码“401”来告诉浏览器需要凭据,此时浏览器会提示用户提供详细信息。从那时起,直到浏览器关闭,它将始终发送凭据而无需进一步提示。
到目前为止我发现的最好的解决方案是(它是一种伪代码,$isLoggedIn
是 http auth 的伪变量):
在“注销”时,只需将一些信息存储到会话中,说明用户实际上已注销。
function logout()
{
//$isLoggedIn = false; //This does not work (point of this question)
$_SESSION['logout'] = true;
}
在我检查身份验证的地方,我扩展了条件:
function isLoggedIn()
{
return $isLoggedIn && !$_SESSION['logout'];
}
会话在某种程度上与 http 身份验证的状态相关联,因此只要用户保持浏览器打开并且 http 身份验证在浏览器中持续存在,用户就会保持注销状态。
也许我错过了重点。
我发现结束 HTTP 身份验证的最可靠方法是关闭浏览器和所有浏览器窗口。您可以使用 Javascript 关闭浏览器窗口,但我认为您不能关闭所有浏览器窗口。
虽然其他人说它不可能从基本的 http 身份验证中注销是正确的,但有一些方法可以实现类似的身份验证。一个明显的方法是使用auth_memcookie。如果您真的想使用此实现基本 HTTP 身份验证(即使用浏览器对话框登录而不是 HTTP 表单) - 只需将身份验证设置为单独的 .htaccess 保护目录,其中包含一个 PHP 脚本,该脚本重定向回用户所在的位置创建内存缓存会话。
这里有很多很棒的 - 复杂的 - 答案。在我的特殊情况下,我找到了一个干净简单的注销解决方法。我还没有在 Edge 中进行测试。在我登录的页面上,我放置了一个与此类似的注销链接:
<a href="https://MyDomainHere.net/logout.html">logout</a>
在该 logout.html 页面的头部(也受 .htaccess 保护)我有一个类似于这样的页面刷新:
<meta http-equiv="Refresh" content="0; url=https://logout:logout@MyDomainHere.net/" />
您将在其中保留“注销”字样以清除为该站点缓存的用户名和密码。
我承认,如果从一开始就需要能够直接登录多个页面,那么每个入口点都需要各自对应的 logout.html 页面。否则,您可以通过在实际登录提示之前在流程中引入额外的网守步骤来集中注销,需要输入短语才能到达登录目的地。
我在一篇文章(https://www.hattonwebsolutions.co.uk/articles/how_to_logout_of_http_sessions)中总结了我的解决方案,但是我使用了 ajax 调用和 2x htaccess 文件(如这个问题中所建议的:How to logout of an HTTP authentication (htaccess)在谷歌浏览器中工作?)。
简而言之 - 你:
这样可以避免在注销文件夹中出现请求另一个用户名的辅助弹出窗口(这会使用户感到困惑)。我的文章使用了 Jquery,但应该可以避免这种情况。