使用HTTP基本身份验证时如何注销用户登录

24
我希望用户能够通过HTTP基本身份验证模式进行登录。
问题是,我也希望他们能够再次退出登录——奇怪的是,浏览器似乎不支持这一点。
这被认为是社交黑客风险——用户离开他们的机器解锁并保持浏览器打开状态,其他人可以轻松地以他们的身份访问该网站。请注意,仅关闭浏览器选项卡是不足以重置令牌的,因此用户可能会忽略这个简单的事情。
因此,我想出了一个解决方法,但它是一个完全混乱的方法:
1)将他们重定向到注销页面
2)在该页面上启动脚本来ajax加载另一个带有虚假凭据的页面:
$j.ajax({
    url: '<%:Url.Action("LogOff401", new { id = random })%>',
    type: 'POST',
    username: '<%:random%>',
    password: '<%:random%>',
    success: function () { alert('logged off'); }
});

3) 第一次请求应该总是返回401状态码(以强制使用新凭证),接下来只接受虚拟凭证:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult LogOff401(string id)
{
    // if we've been passed HTTP authorisation
    string httpAuth = this.Request.Headers["Authorization"];
    if (!string.IsNullOrEmpty(httpAuth) &&
        httpAuth.StartsWith("basic", StringComparison.OrdinalIgnoreCase))
    {
        // build the string we expect - don't allow regular users to pass
        byte[] enc = Encoding.UTF8.GetBytes(id + ':' + id);
        string expected = "basic " + Convert.ToBase64String(enc);

        if (string.Equals(httpAuth, expected, StringComparison.OrdinalIgnoreCase))
        {
            return Content("You are logged out.");
        }
    }

    // return a request for an HTTP basic auth token, this will cause XmlHttp to pass the new header
    this.Response.StatusCode = 401; 
    this.Response.StatusDescription = "Unauthorized";
    this.Response.AppendHeader("WWW-Authenticate", "basic realm=\"My Realm\""); 

    return Content("Force AJAX component to sent header");
}

4) 现在随机字符串凭据已被浏览器接受并缓存。当用户访问另一个页面时,它会尝试使用这些凭据,但会失败,然后提示正确的凭据。

请注意,我的代码示例使用jQuery和ASP.Net MVC,但使用任何技术栈都应该是可能的。

在IE6及以上版本中还有另一种方法:

document.execCommand("ClearAuthenticationCache");

然而,这样会清除所有的身份验证 - 他们退出我的站点后,也会退出他们的电子邮件。所以这个方法行不通。

有没有更好的方法?

我看到了其他问题,但它们是两年前的 - 现在在IE9、FX4、Chrome等浏览器中是否有更好的方法?

如果没有更好的方法,这个方法可靠吗?有没有办法使其更加健壮?


2
HTTP身份验证没有注销的概念。您应该在用户登录时在服务器端创建一个HTTP会话,并让服务器跟踪该会话,例如使用客户端Cookie或WebStorage。要退出,请简单地结束HTTP会话并终止引用它的Cookie/存储。由于HTTP是无状态的,并且对同一服务器的请求可以跨越多个TCP连接,因此在处理身份验证时,大多数站点都是这样操作的。 - Remy Lebeau
我认为浏览器中HTTP身份验证的丑陋界面在很久以前就已经让几乎所有人都使用自己的登录页面(如上所述,带有自己的会话管理)。如果,由于某种天知道的原因在2016年,您想要使用HTTP身份验证,只需保持自己的会话,并对于任何当前未打开会话的用户返回401即可(无需上面原始问题的所有复杂性)。 - John Hascall
@JohnHascall,这基本上只是使用会话作为身份验证,这很好,但如果您没有auth cookie,则可能没有会话cookie,反之亦然。关于是否应该使用HTTP基本身份验证还有一个完全不同的论点(您几乎肯定不应该)。是的,现在是2016年,但许多客户都是大公司和政府,他们行动缓慢 - 只有当旧的廉价技术被迫从他们手中夺走时,他们才会放弃它。 - Keith
@IanBoyd 我不知道什么是理想的 - 基本身份验证每次都会重新发送和检查密码,但为了避免暴力攻击,通常最好使用故意缓慢的密码哈希。如果使用安全、缓慢的密码哈希,则可能希望在完成该步骤后切换到令牌模型的身份验证,以便您不必在每个请求上承担这种开销。 - Keith
@Keith 我的意思是在首次连接服务器时如何获取密码。有了它,您可以使用安全的密码存储系统,如scrypt/bcrypt。当然,一旦您登录,用户代理就可以获得授权或会话cookie。在密码验证方面,基本身份验证是最好的选择,包括Digest、其他自定义头部版本或自定义表单版本。 - Ian Boyd
显示剩余2条评论
1个回答

5
简短的回答是:
目前基于HTTP Basic或Digest身份验证的实现中,没有可靠的程序可以实现“注销”操作。
这种身份验证的工作方式是让客户端在请求中添加一个Authorization头。
如果对于某个资源,服务器不满意提供的凭据(例如,如果没有凭据),它将以“401未经授权”的状态代码进行响应并请求身份验证。为此,它将在响应中提供WWW-Authenticate头。
客户端不需要等待服务器请求身份验证。 它可以根据一些本地假设(例如,来自上次成功尝试的缓存信息)简单地提供一个Authorization头。
虽然您概述的“清除”身份验证信息的方法可能与广泛使用的浏览器(即普通客户端)合作良好,但绝不能保证另一个客户端可能会更加“聪明”,并且仅仅识别出您网站上“注销”页面和其他页面的正确身份验证数据。
使用基于客户端证书的身份验证时也会出现类似的“问题”。 只要没有来自客户端的明确支持,您可能会失败。
因此,如果“注销”是一个问题,请切换到任何基于会话的身份验证。
如果您可以访问服务器端身份验证的实现,您可能能够实现一种功能,即在应用程序级别代码的请求下忽略提供的身份验证信息(如果仍与当前“会话”期间提供的身份验证信息相同)(或提供某些“超时”时间),以便客户端将要求用户提供“新”的凭据(执行新的登录)。

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