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

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

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


当前回答

下面是一个使用jQuery的非常简单的Javascript示例:

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

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

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

其他回答

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

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

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

bobince对这个问题的补充回答是…

使用Ajax,您可以将“注销”链接/按钮连接到Javascript函数。让这个函数发送带有错误用户名和密码的XMLHttpRequest。这应该会得到401。然后设置文档。位置返回到预登录页面。这样,用户将永远不会在注销期间看到额外的登录对话框,也不必记得输入错误的凭据。

实际上,我认为基本身份验证的目的是用于静态页面,而不是用于任何复杂的会话管理或CGI页面。

因此,当需要会话管理时,你应该设计一个经典的“登录表单”来查询用户和密码(可能也是第二个因素)。 CGI表单处理程序应该将成功的身份验证转换为服务器上记住的会话(ID)(在cookie中或作为URI的一部分)。

然后,通过使服务器(和客户端)“忘记”会话。 另一个优点是(即使加密后)用户和密码不会随每个请求一起发送到服务器(而是发送会话ID)。

如果服务器上的会话ID与执行的“最后一个动作”的时间戳相结合,则会话超时可以通过比较该时间戳与当前时间来实现: 如果时间跨度太大,则通过忘记会话ID来“超时”会话。

对无效会话的任何请求都将导致重定向到登录页面(或者如果您想让它更舒服,您可以使用“重新验证表单”再次请求密码)。

作为概念的证明,我实现了一个完全无cookie的会话管理,它完全基于URI(会话ID始终是URI的一部分)。 然而,完整的代码对于这个答案来说太长了。

当希望处理数千个并发会话时,必须特别注意性能。

正如其他人所说,我们需要获得相同的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)页面。否则,您可能会看到旧内容。

以下功能在Firefox 40、Chrome 44、Opera 31和IE 11上均已确认有效。 Bowser用于浏览器检测,也使用jQuery。 —“secUrl”为密码保护区域的url,用户可以从该url退出。 —“redirUrl”为非密码保护区域的url(注销成功页面)。 你可能想增加重定向计时器(目前是200ms)。

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); }