【问题标题】:HTTP authentication logout via PHP通过 PHP 进行 HTTP 身份验证注销
【发布时间】:2010-10-01 18:38:47
【问题描述】:

退出 HTTP 身份验证保护文件夹的正确方法是什么?

有一些变通方法可以实现这一点,但它们具有潜在的危险,因为它们可能存在错误或在某些情况/浏览器中不起作用。这就是为什么我正在寻找正确和干净的解决方案。

【问题讨论】:

  • 请指定注销的目的。这应该是强制注销(用户停用)吗?用户的简单注销功能?还有什么?
  • 我不明白为什么这很重要,但这两种情况都是:基于应用程序内部条件的停用以及典型的注销按钮。请解释为什么它很重要,我将直接将其编辑到问题中。
  • “正确且干净的解决方案”是浏览器拥有自己的注销按钮,点击该按钮后,浏览器将停止发送 Auth 标头...可以做梦,对吧?
  • Web Developer Toolbar 有这样的“按钮”。
  • 约瑟夫所说的:web developer toolbar for Firefox -> Miscellaneous -> Clear Private Data -> HTTP Authentication

标签: php authentication .htaccess http-headers password-protection


【解决方案1】:

亩。 不存在正确的方法,甚至没有一种跨浏览器一致的方法。

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

现有的 HTTP 客户端和用户代理通常保留身份验证 信息无限。 HTTP/1.1。没有提供一个方法 服务器指示客户端丢弃这些缓存的凭据。

另一方面,10.4.2 部分说:

如果请求已包含授权凭据,则 401 响应表明授权已被拒绝 证书。如果 401 响应包含与 之前的响应,并且用户代理已经尝试过 验证至少一次,然后用户应该被呈现 响应中给出的实体,因为该实体可能 包括相关的诊断信息。

换句话说,您也许可以再次显示登录框(正如@Karsten 所说),但浏览器不必满足您的要求 - 所以不要过分依赖这个(错误)功能。

【讨论】:

  • 这是 RFC 中的一个错误。 W3C 懒得修复。好伤心。
  • 正如@Jonathan Hanson 建议的below,您可以将跟踪cookie 与HTTP 身份验证一起使用。这对我来说是最好的方法。
【解决方案2】:

在 Safari 中运行良好的方法。也适用于 Firefox 和 Opera,但有警告。

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

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

【讨论】:

  • 根据 RFC 3986(URI:通用语法)第 3.2.1 节。 (用户信息)不推荐使用user:password@host。在大多数情况下,仅使用 http://logout@yourserver.example.com/ 是不可行的。
  • @andho:是的,这是一个重定向。您应该将其与状态 302 一起使用。
  • 显然,一个简单的指向logout@yourserver.example.com 的链接也可以工作(一个“断开”链接到这个 URL),而不是 PHP 中的一个 http 重定向......有什么缺点吗?
  • 注意:使用相对路径的表单提交在重新登录(使用注销提示登录)后完成时可能会失败,因为地址仍然是logout@yourserver.example.com/path而不是yourserver.example.com/path/跨度>
  • logout@yourserver.example.com 在 Chrome 中没有问题,但在 Firefox 中提示安全问题。 logout:true@yourserver.example.com 不会让 Firefox 成为安全问题。这两个网址都不能在 IE8 中工作:/
【解决方案3】:

简单的答案是您无法可靠地退出 http-authentication。

长答案:
Http-auth(与 HTTP 规范的其余部分一样)意味着是无状态的。因此,“登录”或“注销”并不是一个真正有意义的概念。查看它的更好方法是询问每个 HTTP 请求(并记住页面加载通常是多个请求),“您是否允许执行您请求的操作?”。服务器将每个请求视为新请求,并且与之前的任何请求无关。

浏览器选择记住您在第一个 401 中告诉他们的凭据,并在没有用户明确许可的情况下重新发送它们以处理后续请求。这是为用户提供他们期望的“登录/注销”模型的尝试,但这纯粹是一个杂物。 浏览器在模拟这种状态的持久性。 Web 服务器完全不知道它。

所以“注销”,在http-auth的上下文中纯粹是由浏览器提供的模拟,因此超出了服务器的权限。

是的,有杂物。但是它们破坏了 RESTful-ness(如果这对你有价值的话)并且它们是不可靠的。

如果您绝对需要登录/注销模型来进行站点身份验证,最好的选择是跟踪 cookie,以某种方式(mysql、sqlite、flatfile 等)将状态持久性存储在服务器上.这将需要评估所有请求,例如使用 PHP。

【讨论】:

    【解决方案4】:

    解决方法

    您可以使用 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 的某个路径。

    【讨论】:

    • 以其他用户身份登录是一种解决方法。但这确实有效,值得更多赞扬。
    • 我认为这是最好的答案。正如this 对类似问题的回答中所述,随机化密码可能有一些优势。
    • 这就是我想要的 - 在所有浏览器中都可以正常工作。保持我继承的“注销”页面完好无损。我不一定想使用 JS(也许是不合理的),但其他答案都有跨浏览器问题,而且效果很好。
    • 我无法按照解释的方式完成这项工作。当我回到安全区域时,浏览器再次验证自己,并在标题中发送最后使用的有效凭据。但是,稍加改动它就对我有用。我将 200 OK 响应更改为与安全区域相同的标头,但仅接受“注销:注销”用户/通行证。这样,用户就用这个“注销”用户登录了,这个用户在他回到安全区域时会重试。安全区域拒绝此用户/通行证,因此用户可以更改其凭据。
    • 这不起作用,正如它所解释的那样。在 Chrome 40 和 Firefox 35 中测试。
    【解决方案5】:

    解决方法(不是一个干净、好的(甚至工作!参见 cmets)解决方案):

    一次性禁用他的凭据。

    您可以通过发送适当的标头(如果未登录)将您的 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
    

    所以一次性禁用他的凭据应该是微不足道的。

    【讨论】:

    • 此解决方案的问题在于:您让 IE 知道凭据不正确。它显示带有空字段的登录对话框(不显示存储在密码管理器中的值)。但是当您单击取消并刷新页面时,它会发送存储的凭据,从而再次登录。
    • 投反对票;就像 Josef Sable 评论的那样,这并不能解决手头的问题。
    【解决方案6】:

    分两步从 HTTP Basic Auth 注销

    假设我有一个名为“密码保护”的 HTTP 基本身份验证领域,并且 Bob 已登录。要注销,我发出 2 个 AJAX 请求:

    1. 访问脚本 /logout_step1。它将一个随机临时用户添加到 .htusers 并使用其登录名和密码进行响应。
    2. 访问脚本 /logout_step2 authenticated with the temporary user’s login and password。该脚本删除临时用户并在响应中添加此标头:WWW-Authenticate: Basic realm="Password protected"

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

    【讨论】:

    • 哇!这确实值得为纯粹的创造力 +1,即使这是一件完全疯狂的事情。
    【解决方案7】:

    我对这个问题的解决方案如下。您可以在本页的第二个示例中找到函数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;
    }
    

    【讨论】:

      【解决方案8】:

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

      【讨论】:

      • 我相信当您在 Firefox 中选择“删除私人数据”时,可以选择删除经过身份验证的会话
      • 此外,Firefox 的 Web 开发人员工具栏扩展提供了删除 HTTP 身份验证的功能。但这毫无疑问,因为我们真的不能要求我们的用户下载 FF 扩展或运行神秘的浏览器命令:-)
      • Firefox 退出 HTTP 身份验证的默认方式在“工具”>“清除最近历史记录...”下可用,如复选框“活动登录”。这既不直观,也不允许您仅退出一个域,您总是退出每个页面。
      【解决方案9】:

      我发现消除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');
      }
      

      【讨论】:

        【解决方案10】:

        Trac - 默认情况下 - 也使用 HTTP 身份验证。注销无效且无法修复:

        • 这是 HTTP 身份验证方案本身的问题,我们无法在 Trac 中正确修复它。
        • 目前没有适用于所有主流浏览器的解决方法(JavaScript 或其他)。

        发件人:http://trac.edgewall.org/ticket/791#comment:103

        看起来这个问题没有有效的答案,这个问题在七年前就已经报告过了,而且非常合理:HTTP 是无状态的。请求是否使用身份验证凭据完成。但这是客户端发送请求的问题,而不是接收请求的服务器。服务器只能说请求 URI 是否需要授权。

        【讨论】:

          【解决方案11】:

          我需要重置 .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,不像其他浏览器那样清除身份验证;)

          我在已安装的浏览器上对其进行了测试,一旦关闭,每个浏览器似乎在重新进入时始终需要重新授权。

          【讨论】:

          • 这似乎不起作用,我收到的取消文本没有任何弹出登录框。
          • 原来,发送WWW-Authenticate 导致了问题,摆脱了自动注销的问题。
          • 相反,似乎 NOT 发送WWW-Authenticate 同时在一个浏览器 (Chrome) 中修复问题会导致另一个浏览器 (Firefox) 记住凭据并发送它们在下一个请求中,导致自动重新登录!啊!
          • 然后看看UA,做一个或另一个似乎是一个解决方案
          【解决方案12】:

          这可能不是正在寻找的解决方案,但我是这样解决的。 我有 2 个用于注销过程的脚本。

          注销.php

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

          log.php

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

          这样我不会收到警告并且我的会话被终止

          【讨论】:

          • 这是唯一对我有用的解决方案!在 Firefox 37 和 Chromium 41 上测试
          【解决方案13】:

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

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

          【讨论】:

            【解决方案14】:

            到目前为止我发现的最好的解决方案是(它是一种伪代码,$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 基本身份验证是 RESTful 的,但会话不是。
            【解决方案15】:

            也许我没抓住重点。

            我发现结束 HTTP 身份验证的最可靠方法是关闭浏览器和所有浏览器窗口。您可以使用 Javascript 关闭浏览器窗口,但我认为您不能关闭所有浏览器窗口。

            【讨论】:

            • 仅供参考,如果它是唯一打开的选项卡,某些浏览器不会关闭窗口,所以这一点真的没有实际意义
            • 多年前我的任务是在不关闭窗口的情况下实现注销按钮 :-) 但也许他们不会纠结于“不关闭窗口”。但是,嘿,这是一个简单的解决方案,可能对某人有用,老实说,我当时错过了它。
            【解决方案16】:

            虽然其他人说它不可能从基本的 http 身份验证中注销是正确的,但有一些方法可以实现身份验证,行为类似。一个明显的方法是使用auth_memcookie。如果您真的想使用此实现基本 HTTP 身份验证(即使用浏览器对话框登录而不是 HTTP 表单) - 只需将身份验证设置为单独的 .htaccess 保护目录,其中包含一个 PHP 脚本,该脚本重定向回用户所在的位置创建内存缓存会话。

            【讨论】:

              【解决方案17】:

              这里有很多很棒的——复杂的——答案。在我的特殊情况下,我找到了一个干净简单的注销修复方法。我还没有在 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 页面。否则,您可以通过在实际登录提示之前在流程中引入额外的网守步骤来集中注销,需要输入短语才能到达登录目的地。

              【讨论】:

              • 前进时,这有效,它确实注销,但浏览器返回历史仍然可以重新建立会话。
              【解决方案18】:

              我在一篇文章 (https://www.hattonwebsolutions.co.uk/articles/how_to_logout_of_http_sessions) 中总结了我的解决方案,但是我使用了 ajax 调用和 2x htaccess 文件(如本问题中所建议:How to logout of an HTTP authentication (htaccess) that works in Google Chrome?)。

              简而言之——你:

              1. 在同一 AuthName 上创建一个包含 htaccess 文件的子文件夹,但需要不同的用户
              2. 向页面发送 ajax 请求(使用错误的用户名)(失败),然后触发超时重定向到已注销的页面。

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

              【讨论】:

                猜你喜欢
                • 2021-10-08
                • 2023-03-15
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2011-05-08
                • 2011-06-03
                • 2011-10-26
                • 2014-05-26
                相关资源
                最近更新 更多