如何使用基本身份验证登出网站用户?

350

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

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

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


2
只是好奇。你为什么想要这样做? - DOK
42
切换不同的用户账号登录。 - Marko
24
这是一种标准的社交工程技巧:用户应该能够在浏览器保持开启的情况下退出登录。假设你的一个用户在公共电脑上访问了网站,他们需要明确地注销以防下一个使用者以他们的身份访问该网站。 - Keith
2
@DOK 还有一个问题是它使用户无法注销站点。服务器可以清除授权 cookie,甚至会话 cookie。但当浏览器加载“/”页面时,他们将自动重新登录。 - Ian Boyd
1
如果浏览器显示登录表单,则浏览器本身必须在工具栏的某个位置提供一个注销按钮。我们应该向所有浏览器提交一个功能请求。 - Sergey Ponomarev
显示剩余2条评论
26个回答

260
让用户点击一个链接到 https://log:out@example.com/。这将使用无效的凭据覆盖现有的凭据;使其注销。
该操作通过在URL中发送新凭据实现。 在此情况下,user =“log” password =“out”。

23
为什么这个没有得到更多的赞?对我来说,它似乎是一个简单且行之有效的解决方案。这种方法是否存在已知问题? - amoebe
49
由于安全原因,Chrome 不再支持在 URL 中包含凭证信息,因此这种方法将不再可行。 - Thom
5
这对我有用 :) 我正在使用Chrome版本32.0.1700.102。 - abottoni
8
问题:在使用Chrome 39.0版本时,当我通过这种方式点击注销链接时,Chrome会记住错误的登录凭据,并提示在每个页面加载时输入新的登录凭据,直到我进入https://example.com且没有指定登录凭据来清除Chrome的内存。 - Scott
5
抱歉,我不能在Chrome上使用它来进行HTTPS连接。 - thienkhoi tran
显示剩余17条评论

208

对于bobince的回答的补充...

使用Ajax,您可以将“注销”链接/按钮连接到一个JavaScript函数。使此函数发送带有错误用户名和密码的XMLHttpRequest。这应该会得到一个401的响应。然后将document.location设置回先前的登录页面。这样,用户在注销时将永远不会看到额外的登录对话框,也不必记住输入错误的凭据。


14
好的,针对大多数Web应用程序而言,用户手动输入错误的凭证可能是不可接受的。 - BillMan
1
请确保XMLHttpRequest未设置为异步,否则您可能会发现在注销请求完成之前重定向将发生。 - davidjb
5
你也可以使用相同的技巧来登录。这样,你就可以自定义登录对话框,而无需更改服务器的身份验证方法。这篇文章提供了一些很好的想法:http://www.peej.co.uk/articles/http-auth-with-html-forms.html。 - Stijn de Witt
2
@davidjb 由于同步请求现在被认为是过时的,另一种解决方案可能是在异步请求的回调中重定向用户。 - Hayden Schiff
1
David:Chrome现在允许XHRs这样做,我可以确认它仍然在Chrome金丝雀版本中工作。https://bugs.chromium.org/p/chromium/issues/detail?id=435547 - CpnCrunch
显示剩余2条评论

188

基本认证并不是用来管理登出的。你可以实现,但不能完全自动化。

您需要做的是让用户单击注销链接,并以相应方式发送“401未经授权”的响应,使用与请求登录时发送的普通401相同的领域和在相同的URL文件夹级别上。

他们必须接下来输入错误的凭据,例如空白的用户名和密码,然后返回一个“您已成功退出”的页面。错误/空白的凭据将覆盖先前正确的凭据。

简而言之,登出脚本反转了登录脚本的逻辑,仅在用户 没有通过正确的凭据时返回成功页面。

问题是有多少用户会接受有点奇怪的“不要输入密码”的密码框。尝试自动填充密码的密码管理器也可能会妨碍此处的操作。

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


2
我会尝试这种方法。退出登录的目的(在这种情况下)是为了让用户能够以不同的用户身份登录,因此这是完全可接受的解决方案。至于自动填充密码,使用与否取决于用户。 - Marko
这还是唯一的方式吗?我已经完成了一个可行的ASP.Net MVC和jQuery实现,但我仍然对它不满意:https://dev59.com/RW015IYBdhLWcg3w_Q0_ - Keith
绝妙的解决方案。我结合了您的答案和这篇文章https://dev59.com/BG025IYBdhLWcg3whGax。 - Praesagus
21
W3C在HTML规范方面非常活跃,但HTTP规范却一蹶不振。W3C应该在大约20年前就解决了这个问题。随着REST服务的普及,强大的本地身份验证方法是当今所需。 - Dojo
11
在本地主机上使用Chrome 46浏览器时,似乎无法正常工作。Chrome似乎会保留旧(正确的)密码和您指定的新密码。在导航到注销页面后,Chrome会正确地使用新密码,直到在您网站上的页面上遇到401未经授权错误。在第一个401出现后,Chrome回退到旧(正确的)密码。因此,它似乎并没有删除密码。 - vancan1ty
显示剩余2条评论

81
你可以完全使用JavaScript实现它:
IE(很长一段时间以来)具有用于清除基本身份验证缓存的标准API:
document.execCommand("ClearAuthenticationCache")

当函数工作时应返回true。在其他浏览器上可能返回false,未定义或崩溃。
新的浏览器(自2012年12月起:Chrome,FireFox,Safari)具有“魔法”行为。如果它们看到任何虚假的用户名(比如登出)的成功的基本认证请求,它们会清除凭据缓存,并可能将其设置为该新虚假用户名,因此您需要确保它不是用于查看内容的有效用户名。
以下是一个基本示例:
var p = window.location.protocol + '//'
// current location must return 200 OK for this GET
window.location = window.location.href.replace(p, p + 'logout:password@')

一种“异步”的方式是使用 AJAX 调用,利用 logout 用户名来执行上述操作。例如:
(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*/);

1
这需要特殊的服务器端处理“logout”用户名和/或注销URL吗? - ulidtko
1
@ulidtko 不会,所有的处理都是在客户端进行的。唯一需要特殊处理的情况是,如果一个名为“logout”的用户恰好存在并且恰好拥有生成的密码。在那种几乎不可能发生的情况下,将用户ID更改为系统中不存在的ID。 - davidjb
3
我今天使用了上面的书签小工具,它运行良好。 - David Gleba
2
书签工具在Edge上也可以使用。只需使用 <a href='javascript:......need*/);'>Logout</a> 即可。 - Eric
1
至少使用localhost、https和chrome 87,这只是说“您现在应该已经注销了”,但您仍然完全登录。 - eis
显示剩余4条评论

26

以下函数已经确认在Firefox 40,Chrome 44,Opera 31和IE 11上可用。
Bowser用于浏览器检测,也使用了jQuery。

- secUrl是指密码保护区域的网址,用于注销登录。
- redirUrl是指非密码保护区域的网址(注销成功页面)。
- 您可能希望增加重定向计时器的时间(当前为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);
}


这是最全面的答案。 - belidzs
$.ajax变体为什么是同步的(async: false),而xmlhttp变体是异步的(open()中的true)? - Bowi
1
Chrome现在使用了渲染引擎Blink,因此您必须将(bowser.gecko)更改为(bowser.gecko || bowser.blink) - Bowi
1
为什么Gecko/Blink使用$.ajax,而WebKit使用new XMLHttpRequest?Gecko/Blink不应该也能够使用XMLHttpRequest,而WebKit也能够使用$.ajax吗?我感到困惑。 - RemyNL

14

以下是使用jQuery的非常简单的Javascript示例:

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

    jQuery.get(out).error(function() {
        window.location = to_url;
    });
}

这将使用户退出,而不再显示浏览器的登录框,然后将其重定向到一个已注销页面。


2
window.location= window.location.href.replace(/:///, '://log:out@'); - sebhaase

11

使用基本认证直接做到这一点是不可能的。

HTTP规范中没有机制让服务器告诉浏览器停止发送用户已经提交的凭据。

通常情况下,有一些“技巧”(请参见其他答案),涉及使用XMLHttpRequest发送带有错误凭证的HTTP请求来覆盖最初提供的凭证。


13
理论上看起来是这样,但从其他答案可以看出实践证明不同。 - Stijn de Witt
4
正如你也可以从其他答案中看到的一样,这种方式并不可靠、一致和安全! - jplandrain

10

1
但是用户必须是真实的用户,否则会出现"401未授权"的错误,但是使用后退按钮,我可以继续以先前登录的用户身份工作。已在Abyss web server X1 (2.11.1)上进行了测试。 - user2956477
3
重复答案(请参见Matthew Welborn上面的回答)。 - Skippy le Grand Gourou

9

仅供记录,现在有一个新的HTTP响应头称为Clear-Site-Data。如果您的服务器回复包括一个Clear-Site-Data: "cookies"头,则应删除认证凭据(不仅是cookie)。我在Chrome 77上进行了测试,但这个警告显示在控制台上:

Clear-Site-Data header on 'https://localhost:9443/clear': Cleared data types:
"cookies". Clearing channel IDs and HTTP authentication cache is currently not
supported, as it breaks active network connections.

这些认证凭证并未被移除,因此目前无法实现基本身份验证的注销,但也许将来会有所改善。在其他浏览器上尚未进行测试。

参考资料:

https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Clear-Site-Data

https://www.w3.org/TR/clear-site-data/

https://github.com/w3c/webappsec-clear-site-data

https://caniuse.com/#feat=mdn-http_headers_clear-site-data_cookies


https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Clear-Site-Data - 看起来Safari不喜欢这个。 - James Stevens
在 Firefox 106 上进行了测试,结果相同。当设置 Clear-Site-Data: "cookies"(根据 MDN,这也应该清除身份验证数据)时,控制台会显示 Clear-Site-Data header forced the clean up of “cookies” data,但是 Authentication 标头仍然存在于后续请求中。 - Robin Métral

6

以下内容可适用于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"); 
      });
  });          

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接