English 中文(简体)
通过PHP进行HTTP身份验证注销
原标题:
  • 时间:2009-01-16 07:56:28
  •  标签:

什么是HTTP身份验证受保护文件夹的正确注销方式?

有一些方法可以实现这个目标,但它们可能存在风险,因为它们可能会出现漏洞或在某些情况/浏览器中不起作用。这就是为什么我正在寻找正确而干净的解决方案。

最佳回答

木。没有正确的方法存在,即使在所有浏览器中也没有一种一致的方法。

这是来自HTTP规范(第15.6节)的问题:

Existing HTTP clients and user agents typically retain authentication information indefinitely. HTTP/1.1. does not provide a method for a server to direct clients to discard these cached credentials.

另一方面,第10.4.2节表示:

If the request already included Authorization credentials, then the 401 response indicates that authorization has been refused for those credentials. If the 401 response contains the same challenge as the prior response, and the user agent has already attempted authentication at least once, then the user SHOULD be presented the entity that was given in the response, since that entity might include relevant diagnostic information.

换句话说,你可能可以再次显示登录框(正如@Karsten所说),但浏览器不一定会遵守你的请求,因此不要太依赖这个(误导性)功能。

问题回答

这个方法在Safari中运作良好。在Firefox和Opera中也可以工作,但会有一个警告。

Location: http://logout@yourserver.example.com/

这告诉浏览器使用新的用户名打开URL,覆盖以前的用户名。

简单的答案是你无法可靠地登出http认证。

The long answer:
Http-auth (like the rest of the HTTP spec) is meant to be stateless. So being "logged in" or "logged out" isn t really a concept that makes sense. The better way to see it is to ask, for each HTTP request (and remember a page load is usually multiple requests), "are you allowed to do what you re requesting?". The server sees each request as new and unrelated to any previous requests.

浏览器选择记住您在第一次 401 时告诉它们的凭据,并在随后的请求中未经用户明确许可即重新发送。 这是试图为用户提供他们所期望的“已登录/已注销”模型,但这完全是一个" 修补程序"。 是浏览器模拟了这种状态持久性。 网页服务器完全不知道这一点。

因此,“登出”在http-auth的上下文中纯粹是浏览器提供的模拟,并且在服务器的授权范围外。

是的,有一些临时解决方案。但它们会破坏RESTful的特性(如果对您有价值),而且它们是不可靠的。

如果您绝对需要一个已登录/已退出的模型来进行网站身份验证,最好的选择是使用跟踪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

禁用他的凭据一次应该很容易。

Logout from HTTP Basic Auth in two steps

假设我有一个名为“Password protected”的HTTP基本认证领域,Bob已经登录。要注销,我发起了2个AJAX请求:

  1. Access script /logout_step1. It adds a random temporary user to .htusers and responds with its login and password.
  2. Access script /logout_step2 authenticated with the temporary user’s login and password. The script deletes the temporary user and adds this header on the response: WWW-Authenticate: Basic realm="Password protected"

此时浏览器忘记了Bob的凭证。

我的问题解决方案如下。您可以在此页面的第二个示例中找到函数http_digest_parse$realm$usershttp://php.net/manual/en/features.http-auth.php

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;
}

我找到的唯一有效的方法来清除PHP_AUTH_DIGESTPHP_AUTH_USERPHP_AUTH_PW凭证是调用HTTP/1.1 401 Unauthorized头。

function clear_admin_access(){
    header( HTTP/1.1 401 Unauthorized );
    die( Admin access turned off );
}

通常,一旦浏览器请求用户凭据并向特定网站提供了它们,它就会继续这样做,而无需进一步提示。与客户端清除cookie的各种方式不同,我不知道有类似的方法可以要求浏览器忘记其提供的身份验证凭据。

Trac- 默认情况下- 也使用HTTP身份验证。注销无法正常工作,也无法修复:

  • This is an issue with the HTTP authentication scheme itself, and there s nothing we can do in Trac to fix it properly.
  • There is currently no workaround (JavaScript or other) that works with all major browsers.

从: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;
}
?>

Found it here : http://php.net/manual/en/features.http-auth.php

想不到吧。

许多解决方案都驻留在那个页面上,甚至在底部注明:Lynx不像其他浏览器一样清除身份验证 ;)

我在安装的浏览器上测试过,一旦关闭,每个浏览器似乎都需要重新验证身份才能再次进入。

This might be not the solution that was looked for but i solved it like this. i have 2 scripts for the logout process.

注销.php

<?php
header("Location: http://.@domain.com/日志.php");
?>

日志.php

<?php
header("location: https://google.com");
?>

这样我就不会收到警告,我的会话也不会被终止。

据我所知,在使用htaccess(即基于HTTP的)身份验证时,没有干净的方法来实现“注销”功能。

这是因为这样的身份验证使用HTTP错误代码401告诉浏览器需要凭据,此时浏览器提示用户输入详细信息。从那时起,直到关闭浏览器,它将始终发送凭据而无需进一步提示。

我目前找到的最佳解决方案是(它有点像伪代码,$isLoggedIn 是用于http认证的伪变量):

在“注销”时,只需将一些信息存储到会话中,表明用户实际上已注销。

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脚本,该脚本重定向回来。在创建memcache会话后,用户来到了哪里。

There s a lot of great - complex - answers here. In my particular case i found a clean and simple fix for the logout. I have yet to test in Edge. On my page that I have logged in to, I have placed a logout link similar to this:

<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 调用和 2 个 htaccess 文件(如此问题建议:如何注销在谷歌 Chrome 中有效的 HTTP 验证(htaccess)?)。

简而言之 - 你:

  1. Create a sub folder with an htaccess file on the same AuthName but require a different user
  2. Send an ajax request to the page (with the wrong username) (which fails) and then trigger a timeout redirect to the logged out page.

这样可以避免在注销文件夹中出现第二个弹出窗口,要求另一个用户名(这会让用户感到困惑)。我的文章使用了Jquery,但是应该有可能避免这种情况。

I achieved this with sending a 401 header. This works fine. With PHP

header( HTTP/1.0 401 Unauthorized );




相关问题
热门标签