代码之家  ›  专栏  ›  技术社区  ›  Marko

如何使用基本身份验证从网站注销用户?

  •  237
  • Marko  · 技术社区  · 16 年前

    如果用户使用基本身份验证,是否可以从网站注销用户?

    终止会话是不够的,因为一旦用户通过身份验证,每个请求都包含登录信息,所以下次用户使用相同的凭据访问站点时,将自动登录。

    目前唯一的解决方案是关闭浏览器,但从可用性的角度来看,这是不可接受的。

    18 回复  |  直到 6 年前
        1
  •  152
  •   bobince    16 年前

    基本身份验证不是为管理注销而设计的。你可以这样做,但不是完全自动的。

    你要做的是让用户点击一个注销链接,并发送一个“401未经授权”的响应,使用相同的领域和相同的URL文件夹级别作为正常401发送请求登录。

    他们必须被引导到下一步输入错误的凭证,例如空白的用户名和密码,并且作为响应,您将返回一个“您已成功注销”页面。错误/空白凭据将覆盖以前的正确凭据。

    简而言之,注销脚本反转了登录脚本的逻辑,仅当用户 不是 通过正确的证书。

    问题是有点好奇的“不要输入密码”密码框是否能满足用户的接受。试图自动填充密码的密码管理器也会妨碍您的工作。

    编辑以添加对注释的响应:重新登录是一个稍微不同的问题(除非您明显需要两步注销/登录)。您必须拒绝(401)第一次尝试访问重新登录链接,而不是接受第二次尝试(可能具有不同的用户名/密码)。有几种方法可以做到这一点。一种方法是在注销链接中包含当前用户名(例如/relogin?用户名),并在凭据与用户名匹配时拒绝。

        2
  •  185
  •   system PAUSE    16 年前

    在Bobince的回答之外…

    使用Ajax,您可以将“注销”链接/按钮连接到一个javascript函数。让此函数使用错误的用户名和密码发送xmlhttprequest。这应该是401。然后将document.location设置回预登录页面。这样,用户在注销时就不会看到额外的登录对话框,也不必记住输入错误的凭证。

        3
  •  171
  •   Bergi Ray Nicholus    10 年前

    让用户点击链接 https://log:out@example.com/ . 这将用无效的凭证覆盖现有的凭证;注销它们。

        4
  •  60
  •   ddotsenko    11 年前

    您完全可以用javascript来完成:

    IE有(很长一段时间)用于清除基本身份验证缓存的标准API:

    document.execCommand("ClearAuthenticationCache")
    

    应该在工作时返回true。在其他浏览器上返回“假”、“未定义”或“放大”。

    新浏览器(截至2012年12月:Chrome、Firefox、Safari)具有“魔力”行为。如果他们看到 成功的 使用任何伪造的其他用户名的基本身份验证请求(例如 logout )它们清除凭证缓存,并可能为新的伪造用户名设置它,您需要确保该用户名不是用于查看内容的有效用户名。

    其基本示例是:

    var p = window.location.protocol + '//'
    // current location must return 200 OK for this GET
    window.location = window.location.href.replace(p, p + 'logout:password@')
    

    执行上述操作的“异步”方法是使用 注销 用户名。例子:

    (function(safeLocation){
        var outcome, u, m = "You should be logged out now.";
        // IE has a simple solution for it - API:
        try { outcome = document.execCommand("ClearAuthenticationCache") }catch(e){}
        // Other browsers need a larger solution - AJAX call with special user name - 'logout'.
        if (!outcome) {
            // Let's create an xmlhttp object
            outcome = (function(x){
                if (x) {
                    // the reason we use "random" value for password is 
                    // that browsers cache requests. changing
                    // password effectively behaves like cache-busing.
                    x.open("HEAD", safeLocation || location.href, true, "logout", (new Date()).getTime().toString())
                    x.send("")
                    // x.abort()
                    return 1 // this is **speculative** "We are done." 
                } else {
                    return
                }
            })(window.XMLHttpRequest ? new window.XMLHttpRequest() : ( window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : u ))
        }
        if (!outcome) {
            m = "Your browser is too old or too weird to support log out functionality. Close all windows and restart the browser."
        }
        alert(m)
        // return !!outcome
    })(/*if present URI does not return 200 OK for GET, set some other 200 OK location here*/)
    

    你也可以把它变成书签:

    javascript:(function(c){var a,b="You should be logged out now.";try{a=document.execCommand("ClearAuthenticationCache")}catch(d){}a||((a=window.XMLHttpRequest?new window.XMLHttpRequest:window.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):void 0)?(a.open("HEAD",c||location.href,!0,"logout",(new Date).getTime().toString()),a.send(""),a=1):a=void 0);a||(b="Your browser is too old or too weird to support log out functionality. Close all windows and restart the browser.");alert(b)})(/*pass safeLocation here if you need*/);

        5
  •  15
  •   mthoring    9 年前

    以下功能已确认适用于Firefox 40、Chrome 44、Opera 31和IE 11。
    Bowser 用于浏览器检测,也使用jquery。

    -Securl是要从中注销的密码保护区域的URL。
    -redirl是非密码保护区域(注销成功页面)的URL。
    -您可能希望增加重定向计时器(当前为200毫秒)。

    function logout(secUrl, redirUrl) {
        if (bowser.msie) {
            document.execCommand('ClearAuthenticationCache', 'false');
        } else if (bowser.gecko) {
            $.ajax({
                async: false,
                url: secUrl,
                type: 'GET',
                username: 'logout'
            });
        } else if (bowser.webkit) {
            var xmlhttp = new XMLHttpRequest();
            xmlhttp.open("GET", secUrl, true);
            xmlhttp.setRequestHeader("Authorization", "Basic logout");
            xmlhttp.send();
        } else {
            alert("Logging out automatically is unsupported for " + bowser.name
                + "\nYou must close the browser to log out.");
        }
        setTimeout(function () {
            window.location.href = redirUrl;
        }, 200);
    }
        6
  •  10
  •   Alnitak    9 年前

    这在基本身份验证中不能直接实现。

    在HTTP规范中,没有任何机制可以让服务器告诉浏览器停止发送用户已经提供的凭证。

    有一些“黑客”(参见其他答案),通常涉及使用xmlhttprequest发送一个带有错误凭证的HTTP请求,以覆盖最初提供的请求。

        7
  •  9
  •   Romuald Brunet    10 年前

    下面是一个使用jquery的非常简单的javascript示例:

    function logout(to_url) {
        var out = window.location.href.replace(/:\/\//, '://log:out@');
    
        jQuery.get(out).error(function() {
            window.location = to_url;
        });
    }
    

    此登录用户不显示浏览器登录框而退出,然后将其重定向到 注销

        8
  •  6
  •   Chiedo    9 年前

    其实很简单。

    只需在浏览器中访问以下内容并使用错误的凭据: http://username:password@yourdomain.com

    这应该“让你退出”。

        9
  •  5
  •   Claudio    11 年前

    这适用于IE/Netscape/Chrome:

          function ClearAuthentication(LogOffPage) 
      {
         var IsInternetExplorer = false;    
    
         try
         {
             var agt=navigator.userAgent.toLowerCase();
             if (agt.indexOf("msie") != -1) { IsInternetExplorer = true; }
         }
         catch(e)
         {
             IsInternetExplorer = false;    
         };
    
         if (IsInternetExplorer) 
         {
            // Logoff Internet Explorer
            document.execCommand("ClearAuthenticationCache");
            window.location = LogOffPage;
         }
         else 
         {
            // Logoff every other browsers
        $.ajax({
             username: 'unknown',
             password: 'WrongPassword',
                 url: './cgi-bin/PrimoCgi',
             type: 'GET',
             beforeSend: function(xhr)
                     {
                xhr.setRequestHeader("Authorization", "Basic AAAAAAAAAAAAAAAAAAA=");
             },
    
                     error: function(err)
                     {
                        window.location = LogOffPage;
                 }
        });
         }
      }
    
    
      $(document).ready(function () 
      {
          $('#Btn1').click(function () 
          {
             // Call Clear Authentication 
             ClearAuthentication("force_logout.html"); 
          });
      });          
    
        10
  •  2
  •   Charlie    9 年前
    function logout() {
      var userAgent = navigator.userAgent.toLowerCase();
    
      if (userAgent.indexOf("msie") != -1) {
        document.execCommand("ClearAuthenticationCache", false);
      }
    
      xhr_objectCarte = null;
    
      if(window.XMLHttpRequest)
        xhr_object = new XMLHttpRequest();
      else if(window.ActiveXObject)
        xhr_object = new ActiveXObject("Microsoft.XMLHTTP");
      else
        alert ("Your browser doesn't support XMLHTTPREQUEST");
    
      xhr_object.open ('GET', 'http://yourserver.com/rep/index.php', false, 'username', 'password');
      xhr_object.send ("");
      xhr_object = null;
    
      document.location = 'http://yourserver.com'; 
      return false;
    }
    
        11
  •  2
  •   Sushovan Mukherjee    9 年前
     function logout(url){
        var str = url.replace("http://", "http://" + new Date().getTime() + "@");
        var xmlhttp;
        if (window.XMLHttpRequest) xmlhttp=new XMLHttpRequest();
        else xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
        xmlhttp.onreadystatechange=function()
        {
            if (xmlhttp.readyState==4) location.reload();
        }
        xmlhttp.open("GET",str,true);
        xmlhttp.setRequestHeader("Authorization","Basic xxxxxxxxxx")
        xmlhttp.send();
        return false;
    }
    
        12
  •  1
  •   Amir Mofakhar    9 年前

    将此添加到应用程序:

    @app.route('/logout')
    def logout():
        return ('Logout', 401, {'WWW-Authenticate': 'Basic realm="Login required"'})
    
        13
  •  1
  •   Envek    9 年前

    您所需要的只是在一些注销URL上重定向用户并返回 401 Unauthorized 错误。在错误页面(必须可以在没有基本身份验证的情况下访问),您需要提供到主页的完整链接(包括方案和主机名)。用户将单击此链接,浏览器将再次请求凭据。

    nginx示例:

    location /logout {
        return 401;
    }
    
    error_page 401 /errors/401.html;
    
    location /errors {
        auth_basic off;
        ssi        on;
        ssi_types  text/html;
        alias /home/user/errors;
    }
    

    错误页面 /home/user/errors/401.html :

    <!DOCTYPE html>
    <p>You're not authorised. <a href="<!--# echo var="scheme" -->://<!--# echo var="host" -->/">Login</a>.</p>
    
        14
  •  1
  •   Foad    7 年前

    根据我在上面读到的内容,我得到了一个简单的解决方案,可以在任何浏览器上使用:

    1)在您的注销页面上,您调用一个Ajax到您的登录后端。您的登录后端必须接受注销用户。一旦后端接受,浏览器将清除当前用户并假定为“注销”用户。

    $.ajax({
        async: false,
        url: 'http://your_login_backend',
        type: 'GET',
        username: 'logout'
    });      
    
    setTimeout(function () {
        window.location.href = 'http://normal_index';
    }, 200);
    

    2)现在,当用户返回到正常索引文件时,它将尝试使用用户“注销”在系统中自动输入,第二次必须使用401回复阻止它,以调用登录/密码对话框。

    3)有很多方法可以做到这一点,我创建了两个登录后端,一个接受注销用户,另一个不接受。我的正常登录页面使用不接受的页面,我的注销页面使用接受它的页面。

        15
  •  0
  •   Tomalak    16 年前
    • 使用会话ID(cookie)
    • 使服务器上的会话ID无效
    • 不接受会话ID无效的用户
        16
  •  0
  •   Amit Shah    9 年前

    此javascript必须适用于所有最新版本的浏览器:

    //Detect Browser
    var isOpera = !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0;
        // Opera 8.0+ (UA detection to detect Blink/v8-powered Opera)
    var isFirefox = typeof InstallTrigger !== 'undefined';   // Firefox 1.0+
    var isSafari = Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0;
        // At least Safari 3+: "[object HTMLElementConstructor]"
    var isChrome = !!window.chrome && !isOpera;              // Chrome 1+
    var isIE = /*@cc_on!@*/false || !!document.documentMode; // At least IE6
    var Host = window.location.host;
    
    
    //Clear Basic Realm Authentication
    if(isIE){
    //IE
        document.execCommand("ClearAuthenticationCache");
        window.location = '/';
    }
    else if(isSafari)
    {//Safari. but this works mostly on all browser except chrome
        (function(safeLocation){
            var outcome, u, m = "You should be logged out now.";
            // IE has a simple solution for it - API:
            try { outcome = document.execCommand("ClearAuthenticationCache") }catch(e){}
            // Other browsers need a larger solution - AJAX call with special user name - 'logout'.
            if (!outcome) {
                // Let's create an xmlhttp object
                outcome = (function(x){
                    if (x) {
                        // the reason we use "random" value for password is 
                        // that browsers cache requests. changing
                        // password effectively behaves like cache-busing.
                        x.open("HEAD", safeLocation || location.href, true, "logout", (new Date()).getTime().toString())
                        x.send("");
                        // x.abort()
                        return 1 // this is **speculative** "We are done." 
                    } else {
                        return
                    }
                })(window.XMLHttpRequest ? new window.XMLHttpRequest() : ( window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : u )) 
            }
            if (!outcome) {
                m = "Your browser is too old or too weird to support log out functionality. Close all windows and restart the browser."
            }
            alert(m);
            window.location = '/';
            // return !!outcome
        })(/*if present URI does not return 200 OK for GET, set some other 200 OK location here*/)
    }
    else{
    //Firefox,Chrome
        window.location = 'http://log:out@'+Host+'/';
    }
    
        17
  •  0
  •   Max    7 年前

    我更新了Mthoring的现代铬合金版解决方案:

    function logout(secUrl, redirUrl) {
        if (bowser.msie) {
            document.execCommand('ClearAuthenticationCache', 'false');
        } else if (bowser.gecko) {
            $.ajax({
                async: false,
                url: secUrl,
                type: 'GET',
                username: 'logout'
            });
        } else if (bowser.webkit || bowser.chrome) {
            var xmlhttp = new XMLHttpRequest();
            xmlhttp.open(\"GET\", secUrl, true);
            xmlhttp.setRequestHeader(\"Authorization\", \"Basic logout\");\
            xmlhttp.send();
        } else {
    // http://stackoverflow.com/questions/5957822/how-to-clear-basic-authentication-details-in-chrome
            redirUrl = url.replace('http://', 'http://' + new Date().getTime() + '@');
        }
        setTimeout(function () {
            window.location.href = redirUrl;
        }, 200);
    }
    
        18
  •  0
  •   jwg    6 年前
        function logout(secUrl, redirUrl) {
            if (bowser.msie) {
                document.execCommand('ClearAuthenticationCache', 'false');
            } else if (bowser.gecko) {
                $.ajax({
                    async: false,
                    url: secUrl,
                    type: 'GET',
                    username: 'logout'
                });
            } else if (bowser.webkit) {
                var xmlhttp = new XMLHttpRequest();
                xmlhttp.open("GET", secUrl, true);
                xmlhttp.setRequestHeader("Authorization", "Basic logout");
                xmlhttp.send();
            } else {
                alert("Logging out automatically is unsupported for " + bowser.name
                    + "\nYou must close the browser to log out.");
            }
            setTimeout(function () {
                window.location.href = redirUrl;
            }, 200);
        }

    我试着用下面的方法来使用上面的内容。

    ?php
        ob_start();
        session_start();
        require_once 'dbconnect.php';
    
        // if session is not set this will redirect to login page
        if( !isset($_SESSION['user']) ) {
            header("Location: index.php");
            exit;
        }
        // select loggedin users detail
        $res=mysql_query("SELECT * FROM users WHERE userId=".$_SESSION['user']);
        $userRow=mysql_fetch_array($res);
    ?>
    <!DOCTYPE html>
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Welcome - <?php echo $userRow['userEmail']; ?></title>
    <link rel="stylesheet" href="assets/css/bootstrap.min.css" type="text/css"  />
    <link rel="stylesheet" href="style.css" type="text/css" />
    
        <script src="assets/js/bowser.min.js"></script>
    <script>
    //function logout(secUrl, redirUrl)
    //bowser = require('bowser');
    function logout(secUrl, redirUrl) {
    alert(redirUrl);
        if (bowser.msie) {
            document.execCommand('ClearAuthenticationCache', 'false');
        } else if (bowser.gecko) {
            $.ajax({
                async: false,
                url: secUrl,
                type: 'GET',
                username: 'logout'
            });
        } else if (bowser.webkit) {
            var xmlhttp = new XMLHttpRequest();
            xmlhttp.open("GET", secUrl, true);
            xmlhttp.setRequestHeader("Authorization", "Basic logout");
            xmlhttp.send();
        } else {
            alert("Logging out automatically is unsupported for " + bowser.name
                + "\nYou must close the browser to log out.");
        }
        window.location.assign(redirUrl);
        /*setTimeout(function () {
            window.location.href = redirUrl;
        }, 200);*/
    }
    
    
    function f1()
        {
           alert("f1 called");
           //form validation that recalls the page showing with supplied inputs.    
        }
    </script>
    </head>
    <body>
    
        <nav class="navbar navbar-default navbar-fixed-top">
          <div class="container">
            <div class="navbar-header">
              <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
              </button>
              <a class="navbar-brand" href="http://www.codingcage.com">Coding Cage</a>
            </div>
            <div id="navbar" class="navbar-collapse collapse">
              <ul class="nav navbar-nav">
                <li class="active"><a href="http://www.codingcage.com/2015/01/user-registration-and-login-script-using-php-mysql.html">Back to Article</a></li>
                <li><a href="http://www.codingcage.com/search/label/jQuery">jQuery</a></li>
                <li><a href="http://www.codingcage.com/search/label/PHP">PHP</a></li>
              </ul>
              <ul class="nav navbar-nav navbar-right">
    
                <li class="dropdown">
                  <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
                  <span class="glyphicon glyphicon-user"></span>&nbsp;Hi' <?php echo $userRow['userEmail']; ?>&nbsp;<span class="caret"></span></a>
                  <ul class="dropdown-menu">
                    <li><a href="logout.php?logout"><span class="glyphicon glyphicon-log-out"></span>&nbsp;Sign Out</a></li>
                  </ul>
                </li>
              </ul>
            </div><!--/.nav-collapse -->
          </div>
        </nav> 
    
        <div id="wrapper">
    
        <div class="container">
    
            <div class="page-header">
            <h3>Coding Cage - Programming Blog</h3>
            </div>
    
            <div class="row">
            <div class="col-lg-12" id="div_logout">
            <h1 onclick="logout(window.location.href, 'www.espncricinfo.com')">MichaelA1S1! Click here to see log out functionality upon click inside div</h1>
            </div>
            </div>
    
        </div>
    
        </div>
    
        <script src="assets/jquery-1.11.3-jquery.min.js"></script>
        <script src="assets/js/bootstrap.min.js"></script>
    
    
    </body>
    </html>
    <?php ob_end_flush(); ?>
    

    但它只会将你重定向到新的位置。没有注销。