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

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

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


当前回答

这适用于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"); 
      });
  });          

其他回答

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

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

接下来,他们必须被引导输入错误的凭证。一个空白的用户名和密码,作为回应,您发回一个“您已成功注销”页面。错误的/空白的凭证将覆盖之前的正确凭证。

简而言之,登出脚本颠倒了登录脚本的逻辑,只有在用户没有传递正确的凭据时才返回成功页面。

问题是这个有点奇怪的“不要输入密码”的密码框是否能让用户接受。试图自动填充密码的密码管理器也会在这里起到阻碍作用。

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

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

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>

发送https://invalid_login@hostname在任何地方都可以工作,除了Mac上的Safari(好吧,没有勾选Edge,但也应该工作)。

当用户在HTTP基本身份验证弹出框中选择“记住密码”时,Safari中的注销不工作。在这种情况下,密码存储在Keychain Access (Finder > Applications > Utilities > Keychain Access(或CMD+SPACE并键入“Keychain Access”))中。发送https://invalid_login@hostname不会影响钥匙链访问,所以这个复选框不可能在Mac上的Safari上注销。至少它是如何为我工作的。

MacOS Mojave (10.14.6), Safari 12.1.2。

下面的代码在Firefox(73)、Chrome(80)和Safari(12)中运行良好。当用户导航到登出页面时,执行代码并删除凭据。

    //It should return 401, necessary for Safari only
    const logoutUrl = 'https://example.com/logout'; 
    const xmlHttp = new XMLHttpRequest();
    xmlHttp.open('POST', logoutUrl, true, 'logout');
    xmlHttp.send();

此外,由于某些原因,Safari不会在HTTP基本身份验证弹出框中保存凭据,即使选择了“记住密码”。其他浏览器可以正确地做到这一点。

正如其他人所说,我们需要获得相同的URL并发送一个错误(例如401:StatusUnauthorized之类的),仅此而已。

我使用Get方法让它知道我需要登出,

下面是一个使用golang进行写作的完整示例。

package main

import (
    "crypto/subtle"
    "fmt"
    "log"
    "net/http"
)

func BasicAuth(username, password, realm string, handlerFunc http.HandlerFunc) http.HandlerFunc {

    return func(w http.ResponseWriter, r *http.Request) {
        queryMap := r.URL.Query()
        if _, ok := queryMap["logout"]; ok { // localhost:8080/public/?logout
            w.WriteHeader(http.StatusUnauthorized) // 401
            _, _ = w.Write([]byte("Success logout!\n"))
            return
        }

        user, pass, ok := r.BasicAuth()

        if !ok ||
            subtle.ConstantTimeCompare([]byte(user), []byte(username)) != 1 ||
            subtle.ConstantTimeCompare([]byte(pass), []byte(password)) != 1 {
            // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/WWW-Authenticate
            w.Header().Set("WWW-Authenticate", `Basic realm="`+realm+`", charset="UTF-8"`)
            w.WriteHeader(http.StatusUnauthorized)
            _, _ = w.Write([]byte("Unauthorised.\n"))
            return
        }

        handlerFunc(w, r)
    }
}

type UserInfo struct {
    name string
    psw  string
}

func main() {

    portNumber := "8080"
    guest := UserInfo{"guest", "123"}

    // localhost:8080/public/  -> ./public/everyone
    publicHandler := http.StripPrefix(
        "/public/", http.FileServer(http.Dir("./public/everyone")),
    )

    publicHandlerFunc := func(w http.ResponseWriter, r *http.Request) {
        switch r.Method {
        case http.MethodGet:
            publicHandler.ServeHTTP(w, r)
        /*
            case http.MethodPost:
            case http.MethodPut:
            case http.MethodDelete:
        */
        default:
            return
        }
    }

    http.HandleFunc("/public/",
        BasicAuth(guest.name, guest.psw, "Please enter your username and password for this site",
            publicHandlerFunc),
    )

    log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", portNumber), nil))
}

当您已经登出时,您需要刷新(F5)页面。否则,您可能会看到旧内容。

这适用于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"); 
      });
  });