简体   繁体   English

通过 PHP 进行 HTTP 身份验证注销

[英]HTTP authentication logout via PHP

What is the correct way to log out of HTTP authentication protected folder?注销 HTTP 身份验证保护文件夹的正确方法是什么?

There are workarounds that can achieve this, but they are potentially dangerous because they can be buggy or don't work in certain situations / browsers.有一些变通方法可以实现这一点,但它们具有潜在的危险,因为它们可能存在错误或在某些情况/浏览器中不起作用。 That is why I am looking for correct and clean solution.这就是为什么我正在寻找正确和干净的解决方案。

Mu.亩。 No correct way exists , not even one that's consistent across browsers.不存在正确的方法,甚至不存在跨浏览器一致的方法。

This is a problem that comes from the HTTP specification (section 15.6):这是来自HTTP 规范(第 15.6 节)的问题:

Existing HTTP clients and user agents typically retain authentication information indefinitely.现有的 HTTP 客户端和用户代理通常会无限期地保留身份验证信息。 HTTP/1.1. HTTP/1.1。 does not provide a method for a server to direct clients to discard these cached credentials.没有为服务器提供一种方法来指示客户端丢弃这些缓存的凭据。

On the other hand, section 10.4.2 says:另一方面,第10.4.2节说:

If the request already included Authorization credentials, then the 401 response indicates that authorization has been refused for those credentials.如果请求已包含授权凭据,则 401 响应表示已拒绝对这些凭据进行授权。 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.如果 401 响应包含与先前响应相同的质询,并且用户代理已经尝试过至少一次身份验证,那么应该向用户呈现响应中给出的实体,因为该实体可能包含相关诊断信息。

In other words, you may be able to show the login box again (as @Karsten says), but the browser doesn't have to honor your request - so don't depend on this (mis)feature too much.换句话说,您可以再次显示登录框(如@Karsten所说),但浏览器不必满足您的请求- 所以不要过分依赖此(错误)功能。

Method that works nicely in Safari.在 Safari 中运行良好的方法。 Also works in Firefox and Opera, but with a warning.也适用于 Firefox 和 Opera,但有警告。

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

This tells browser to open URL with new username, overriding previous one.这告诉浏览器使用新用户名打开 URL,覆盖之前的用户名。

The simple answer is that you can't reliably log out of http-authentication.简单的答案是您无法可靠地注销 http 身份验证。

The long answer:长答案:
Http-auth (like the rest of the HTTP spec) is meant to be stateless. Http-auth(与 HTTP 规范的其余部分一样)是无状态的。 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?".更好的查看方式是针对每个 HTTP 请求(记住一个页面加载通常是多个请求)询问“您是否被允许执行您请求的操作?”。 The server sees each request as new and unrelated to any previous requests.服务器将每个请求视为新的并且与之前的任何请求无关。

Browsers have chosen to remember the credentials you tell them on the first 401, and re-send them without the user's explicit permission on subsequent requests.浏览器选择记住您在第一个 401 时告诉他们的凭据,并在没有用户对后续请求的明确许可的情况下重新发送它们。 This is an attempt at giving the user the "logged in/logged out" model they expect, but it's purely a kludge.这是为用户提供他们期望的“登录/注销”模型的尝试,但这纯粹是一种拼凑。 It's the browser that's simulating this persistence of state.浏览器在模拟这种状态持久性。 The web server is completely unaware of it. Web 服务器完全不知道它。

So "logging out", in the context of http-auth is purely a simulation provided by the browser, and so outside the authority of the server.因此,在 http-auth 的上下文中,“注销”纯粹是浏览器提供的模拟,因此超出了服务器的权限。

Yes, there are kludges.是的,有kludges。 But they break RESTful-ness (if that's of value to you) and they are unreliable.但是它们破坏了 RESTful-ness(如果这对您有价值)并且它们不可靠。

If you absolutely require a logged-in/logged-out model for your site authentication, the best bet is a tracking cookie, with the persistence of state stored on the server in some manner (mysql, sqlite, flatfile, etc).如果您绝对需要登录/注销模型来进行站点身份验证,最好的选择是跟踪 cookie,以某种方式(mysql、sqlite、flatfile 等)将状态持久性存储在服务器上。 This will require all requests to be evaluated, for instance, with PHP.这将需要评估所有请求,例如使用 PHP。

Workaround解决方法

You can do this using Javascript:您可以使用 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>

What is done above is:上面所做的是:

  • for IE - just clear auth cache and redirect somewhere对于 IE - 只需清除身份验证缓存并重定向到某处

  • for other browsers - send an XMLHttpRequest behind the scenes with 'logout' login name and password.对于其他浏览器- 在后台发送带有“注销”登录名和密码的 XMLHttpRequest。 We need to send it to some path that will return 200 OK to that request (ie it shouldn't require HTTP authentication).我们需要将它发送到某个路径,该路径将向该请求返回 200 OK(即它不应该要求 HTTP 身份验证)。

Replace '/where/to/redirect' with some path to redirect to after logging out and replace '/path/that/will/return/200/OK' with some path on your site that will return 200 OK.'/where/to/redirect'替换为注销后重定向到的路径,并将'/path/that/will/return/200/OK'替换为您网站上将返回 200 OK 的路径。

Workaround (not a clean, nice (or even working: see comments) solution):解决方法(不是一个干净的、好的(甚至是工作的:见评论)解决方案):

Disable his credentials one time.禁用他的凭据一次。

You can move your HTTP authentication logic to PHP by sending the appropriate headers (if not logged in):您可以通过发送适当的标头(如果未登录)将 HTTP 身份验证逻辑移至 PHP:

Header('WWW-Authenticate: Basic realm="protected area"');
Header('HTTP/1.0 401 Unauthorized');

And parsing the input with:并解析输入:

$_SERVER['PHP_AUTH_USER'] // httpauth-user
$_SERVER['PHP_AUTH_PW']   // httpauth-password

So disabling his credentials one time should be trivial.因此,一次禁用他的凭据应该是微不足道的。

Logout from HTTP Basic Auth in two steps分两步退出 HTTP Basic Auth

Let's say I have a HTTP Basic Auth realm named “Password protected”, and Bob is logged in. To log out I make 2 AJAX requests:假设我有一个名为“Password protected”的 HTTP Basic Auth 领域,Bob 已登录。要注销,我发出 2 个 AJAX 请求:

  1. Access script /logout_step1.访问脚本 /logout_step1。 It adds a random temporary user to.htusers and responds with its login and password.它将一个随机的临时用户添加到 .htusers 并使用其登录名和密码进行响应。
  2. Access script /logout_step2 authenticated with the temporary user's login and password .访问脚本 /logout_step2 使用临时用户的登录名和密码进行身份验证。 The script deletes the temporary user and adds this header on the response: WWW-Authenticate: Basic realm="Password protected"该脚本删除临时用户并在响应中添加此标头: WWW-Authenticate: Basic realm="Password protected"

At this point browser forgot Bob's credentials.此时浏览器忘记了 Bob 的凭据。

My solution to the problem is the following.我对问题的解决方案如下。 You can find the function http_digest_parse , $realm and $users in the second example of this page: http://php.net/manual/en/features.http-auth.php .您可以在本页的第二个示例中找到函数http_digest_parse$realm$users :http: //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;
}

Typically, once a browser has asked the user for credentials and supplied them to a particular web site, it will continue to do so without further prompting.通常,一旦浏览器要求用户提供凭据并将其提供给特定网站,它将继续这样做而无需进一步提示。 Unlike the various ways you can clear cookies on the client side, I don't know of a similar way to ask the browser to forget its supplied authentication credentials.与您可以在客户端清除 cookie 的各种方法不同,我不知道有类似的方法可以让浏览器忘记其提供的身份验证凭据。

The only effective way I've found to wipe out the PHP_AUTH_DIGEST or PHP_AUTH_USER AND PHP_AUTH_PW credentials is to call the header HTTP/1.1 401 Unauthorized .我发现清除PHP_AUTH_DIGESTPHP_AUTH_USER AND PHP_AUTH_PW凭据的唯一有效方法是调用标头HTTP/1.1 401 Unauthorized

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

This might be not the solution that was looked for but i solved it like this.这可能不是正在寻找的解决方案,但我是这样解决的。 i have 2 scripts for the logout process.我有 2 个用于注销过程的脚本。

logout.php注销.php

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

log.php日志.php

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

This way i dont get a warning and my session is terminated这样我就不会收到警告并且我的会话已终止

Trac - by default - uses HTTP Authentication as well. Trac - 默认情况下 - 也使用 HTTP 身份验证。 Logout does not work and can not be fixed:注销不起作用,无法修复:

  • This is an issue with the HTTP authentication scheme itself, and there's nothing we can do in Trac to fix it properly.这是 HTTP 身份验证方案本身的问题,我们无法在 Trac 中正确修复它。
  • There is currently no workaround (JavaScript or other) that works with all major browsers.目前没有适用于所有主要浏览器的解决方法(JavaScript 或其他)。

From: http://trac.edgewall.org/ticket/791#comment:103来自: http ://trac.edgewall.org/ticket/791#comment:103

Looks like that there is no working answer to the question, that issue has been reported seven years ago and it makes perfect sense: HTTP is stateless.看起来这个问题没有有效的答案,这个问题在七年前就已经被报道过,它非常有道理:HTTP 是无状态的。 Either a request is done with authentication credentials or not.请求是否使用身份验证凭据完成。 But that's a matter of the client sending the request, not the server receiving it.但这是客户端发送请求的问题,而不是服务器接收请求的问题。 The server can only say if a request URI needs authorization or not.服务器只能说明请求 URI 是否需要授权。

I needed to reset .htaccess authorization so I used this:我需要重置 .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在这里找到它:http: //php.net/manual/en/features.http-auth.php

Go figure.去搞清楚。

A number of solutions reside on that page and it even notes at the bottom: Lynx, doesn't clear the auth like other browsers;)该页面上有许多解决方案,它甚至在底部注释:Lynx,不像其他浏览器那样清除身份验证;)

I tested it out on my installed browsers and once closed, each browser seems like it consistently requires reauth on reentry.我在我安装的浏览器上测试了它,一旦关闭,每个浏览器似乎都需要在重新进入时重新授权。

Maybe I'm missing the point.也许我错过了重点。

The most reliable way I've found to end HTTP Authentication is to close the browser and all browser windows.我发现结束 HTTP 身份验证的最可靠方法是关闭浏览器和所有浏览器窗口。 You can close a browser window using Javascript but I don't think you can close all browser windows.您可以使用 Javascript 关闭浏览器窗口,但我认为您无法关闭所有浏览器窗口。

AFAIK, there's no clean way to implement a "logout" function when using htaccess (ie HTTP-based) authentication. AFAIK,在使用 htaccess(即基于 HTTP 的)身份验证时,没有干净的方法来实现“注销”功能。

This is because such authentication uses the HTTP error code '401' to tell the browser that credentials are required, at which point the browser prompts the user for the details.这是因为此类身份验证使用 HTTP 错误代码“401”告诉浏览器需要凭据,此时浏览器会提示用户提供详细信息。 From then on, until the browser is closed, it will always send the credentials without further prompting.从那时起,直到浏览器关闭,它将始终发送凭据而无需进一步提示。

The best solution I found so far is (it is sort of pseudo-code, the $isLoggedIn is pseudo variable for http auth):到目前为止我找到的最好的解决方案是(它是一种伪代码, $isLoggedIn是 http auth 的伪变量):

At the time of "logout" just store some info to the session saying that user is actually logged out.在“注销”时,只需将一些信息存储到会话中,说明用户实际上已注销。

function logout()
{
  //$isLoggedIn = false; //This does not work (point of this question)
  $_SESSION['logout'] = true;
}

In the place where I check for authentication I expand the condition:在我检查身份验证的地方,我扩展了条件:

function isLoggedIn()
{
  return $isLoggedIn && !$_SESSION['logout'];
}

Session is somewhat linked to the state of http authentication so user stays logged out as long as he keeps the browser open and as long as http authentication persists in the browser.会话在某种程度上与 http 身份验证的状态相关联,因此只要用户保持浏览器打开并且只要 http 身份验证在浏览器中持续存在,用户就会保持注销状态。

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.我还没有在 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>

And in the head of that logout.html page (which is also protected by the .htaccess) I have a page refresh similar to this:在该 logout.html 页面(也受 .htaccess 保护)的头部,我有一个类似于此的页面刷新:

<meta http-equiv="Refresh" content="0; url=https://logout:logout@MyDomainHere.net/" />

Where you would leave the words "logout" in place to clear the username and password cached for the site.您将保留“注销”字样的位置,以清除为站点缓存的用户名和密码。

I will admit that if multiple pages needed to be able to be directly logged in to from the beginning, each of those points of entry would need their own corresponding logout.html page.我承认,如果多个页面需要能够从一开始就直接登录,那么每个入口点都需要自己对应的 logout.html 页面。 Otherwise you could centralize the logout by introducing an additional gatekeeper step into the process before the actual login prompt, requiring entry of a phrase to reach a destination of login.否则,您可以通过在实际登录提示之前在流程中引入额外的网守步骤来集中注销,需要输入一个短语才能到达登录目的地。

I have summarised my solution in an article ( https://www.hattonwebsolutions.co.uk/articles/how_to_logout_of_http_sessions ) however I have used an ajax call and 2x htaccess files (as suggested in this question: How to logout of an HTTP authentication (htaccess) that works in Google Chrome? ).我在一篇文章 ( https://www.hattonwebsolutions.co.uk/articles/how_to_logout_of_http_sessions ) 中总结了我的解决方案,但是我使用了 ajax 调用和 2x htaccess 文件(如这个问题中所建议的: How to logout of an HTTP authentication (htaccess) 在 Google Chrome 中有效吗? )。

In short - you:简而言之——你:

  1. Create a sub folder with an htaccess file on the same AuthName but require a different user在相同的 AuthName 上创建一个包含 htaccess 文件的子文件夹,但需要不同的用户
  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.向页面发送 ajax 请求(使用错误的用户名)(失败),然后触发超时重定向到已注销的页面。

This avoids having a secondary popup in the logout folder requesting another username (which would confuse users).这避免了在注销文件夹中出现二次弹出窗口请求另一个用户名(这会使用户感到困惑)。 My article uses Jquery but it should be possible to avoid this.我的文章使用了 Jquery,但应该可以避免这种情况。

While the others are correct in saying that its impossible to logout from basic http authentication there are ways to implement authentication which behave similarly.虽然其他人说不可能从基本的 http 身份验证注销是正确的,但有一些方法可以实现类似的身份验证。 One obvious appeoach is to use auth_memcookie .一个明显的 appeoach 是使用auth_memcookie If you really want to implement Basic HTTP authentication (ie use the browser dialogs for logging in trather than an HTTP form) using this - just set the authentication to a seperate .htaccess protected directory containing a PHP script which redirects back where te user came after createing the memcache session.如果您真的想实现基本 HTTP 身份验证(即使用浏览器对话框而不是 HTTP 表单登录),只需将身份验证设置为一个单独的 .htaccess 保护目录,其中包含一个 PHP 脚本,该脚本重定向回用户所在的位置创建内存缓存会话。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM