89

我的 Web 应用程序有一个登录页面,该页面通过 AJAX 调用提交身份验证凭据。如果用户输入了正确的用户名和密码,一切都很好,但如果不是,则会发生以下情况:

  1. Web 服务器确定尽管请求包含格式正确的 Authorization 标头,但标头中的凭据未成功进行身份验证。
  2. Web 服务器返回一个 401 状态代码,并包含一个或多个 WWW-Authenticate 标头,其中列出了支持的身份验证类型。
  3. 浏览器检测到我对 XMLHttpRequest 对象的调用的响应是 401,并且响应包括 WWW-Authenticate 标头。然后它会弹出一个身份验证对话框,再次询问用户名和密码。

这一切都很好,直到第 3 步。我不想弹出对话框,我想在我的 AJAX 回调函数中处理 401 响应。(例如,通过在登录页面上显示错误消息。)当然,我希望用户重新输入他们的用户名和密码,但我希望他们看到我友好、令人放心的登录表单,而不是浏览器的丑陋默认身份验证对话框。

顺便说一句,我无法控制服务器,因此不能让它返回自定义状态代码(即 401 以外的代码)。

有什么办法可以抑制身份验证对话框?特别是,我可以在 Firefox 2 或更高版本中取消“需要身份验证”对话框吗?有什么方法可以抑制 IE 6 及更高版本中的 Connect to [host]对话框?


编辑
来自作者的附加信息(9 月 18 日):
我应该补充一点,弹出的浏览器身份验证对话框的真正问题是它向用户提供的信息不足。

用户刚刚通过登录页面上的表单输入了用户名和密码,他认为他已经正确输入了它们,并且他点击了提交按钮或回车。他的期望是他将被带到下一页,或者可能被告知他输入的信息有误,应该再试一次。然而,他却看到了一个意想不到的对话框。

该对话框不承认他刚刚输入了用户名和密码这一事实。它没有明确说明存在问题并且他应该再试一次。取而代之的是,对话框会向用户显示诸如“网站说:' [realm] '”之类的神秘信息。其中[realm]是只有程序员才会喜欢的简短领域名称。

Web 浏览器设计者注意:如果对话框本身更加用户友好,没有人会问如何抑制身份验证对话框。我做登录表单的全部原因是我们的产品管理团队正确地认为浏览器的身份验证对话框很糟糕。

4

12 回答 12

53

我在这里遇到了同样的问题,我公司的后端工程师实施了一种显然被认为是一种好习惯的行为:当对 URL 的调用返回 401 时,如果客户端设置了标头X-Requested-With: XMLHttpRequest,则服务器将www-authenticate标头放入其回复。

副作用是不会出现默认的身份验证弹出窗口。

确保您的 API 调用将X-Requested-With标头设置为XMLHttpRequest. 如果是这样,除了根据这个良好实践更改服务器行为之外,别无他法……

于 2013-11-26T15:31:43.837 回答
19

当同时满足以下两个条件时,浏览器会弹出登录提示:

  1. HTTP 状态为 401
  2. WWW-Authenticate响应中存在标头

如果您可以控制 HTTP 响应,那么您可以WWW-Authenticate从响应中删除标头,并且浏览器不会弹出登录对话框。

如果您无法控制响应,则可以设置代理以WWW-Authenticate从响应中过滤掉标头。

据我所知(如果我错了,请随时纠正我),一旦浏览器收到WWW-Authenticate标题,就没有办法阻止登录提示。

于 2015-03-16T16:47:25.100 回答
17

我认为这是不可能的——如果你使用浏览器的 HTTP 客户端实现,它总是会弹出那个对话框。我想到了两个黑客:

  1. 也许 Flash 以不同的方式处理这个问题(我还没有尝试过),所以让 Flash 电影发出请求可能会有所帮助。

  2. 您可以为您在自己的服务器上访问的服务设置一个“代理”,并让它稍微修改身份验证标头,以便浏览器无法识别它们。

于 2008-09-18T07:55:54.493 回答
8

我意识到这个问题及其答案已经很老了。但是,我最终来到了这里。也许其他人也会。

如果您有权访问返回 401 的 Web 服务的代码。在这种情况下,只需将服务更改为返回 403(禁止访问)而不是 401。浏览器不会提示输入凭据以响应 403。403 是未经授权的特定资源的经过身份验证的用户的正确代码。这似乎是OP的情况。

从 403 上的 IETF 文档:

接收到不足以获得访问权限的有效凭据的服务器应该使用 403(禁止访问)状态码进行响应

于 2016-10-05T15:13:29.990 回答
4

在 Mozilla 中,您可以在创建 XMLHttpRequest 对象时使用以下脚本来实现它:

xmlHttp=new XMLHttpRequest();
xmlHttp.mozBackgroundRequest = true;
xmlHttp.open("GET",URL,true,USERNAME,PASSWORD);
xmlHttp.send(null);

第二行阻止对话框....

于 2008-10-04T16:41:20.693 回答
2

您使用什么服务器技术,是否有用于身份验证的特定产品?

由于浏览器只是在做它的工作,我相信你必须改变服务器端的东西才能不返回 401 状态码。这可以使用自定义身份验证表单来完成,当身份验证失败时,只需再次返回表单。

于 2008-09-17T18:34:20.037 回答
2

在 Mozilla 领域,将 XMLHttpRequest ( docs )的 mozBackgroundRequest 参数设置为 true 会抑制这些对话框并导致请求失败。但是,我不知道跨浏览器的支持有多好(包括那些失败请求的错误信息的质量是否跨浏览器非常好。)

于 2009-01-30T04:08:14.387 回答
2

jan.vdbergh 有道理,如果您可以将服务器端的 401 更改为另一个状态码,浏览器将不会捕获并绘制弹出窗口。另一种解决方案可能是将 WWW-Authenticate 标头更改为另一个自定义标头。我不相信为什么不同的浏览器不支持它,在几个版本的 Firefox 中我们可以使用 mozBackgroundRequest 进行 xhr 请求,但是在其他浏览器中呢?在这里, Chromium 中有一个与此问题相关的有趣链接。

于 2012-07-26T09:54:20.633 回答
1

我对 MVC 5 和 VPN 也有同样的问题,每当我们在 DMZ 之外使用 VPN 时,我们发现自己必须回答这个浏览器消息。使用.net,我只需使用处理错误的路由

<customErrors defaultRedirect="~/Error"  >
  <error statusCode="401" redirect="~/Index"/>
</customErrors>

到目前为止,它一直有效,因为主控制器下的 Index 操作验证了用户。此操作中的视图(如果登录不成功)具有登录控件,我使用这些登录控件通过传递到目录服务的 LDAP 查询来登录用户:

      DirectoryEntry entry = new DirectoryEntry("LDAP://OurDomain");
      DirectorySearcher Dsearch = new DirectorySearcher(entry);
      Dsearch.Filter = "(SAMAccountName=" + UserID + ")";
      Dsearch.PropertiesToLoad.Add("cn");

虽然到目前为止它运行良好,但我必须让您知道我仍在测试它并且上面的代码没有理由运行,因此它可能会被删除......测试目前包括尝试发现第二组的情况的代码有更多用处。同样,这是一项正在进行中的工作,但由于它可能会有所帮助或让你的大脑思考一些想法,所以我决定现在添加它......一旦完成所有测试,我将使用最终结果更新它。

于 2015-05-28T15:57:42.280 回答
1

我正在使用 Node、Express 和 Passport,并且遇到了同样的问题。我通过将www-authenticate标头显式设置为空字符串来使其工作。就我而言,它看起来像这样:

(err, req, res, next) => {
  if (err) {
    res._headers['www-authenticate'] = ''
    return res.json(err)
  }
}

我希望对某人有所帮助!

于 2017-10-18T00:14:08.870 回答
0

对于那些不喜欢 C# 的人来说,这里ActionAttribute是返回400而不是401, 和 'swallows' Basic auth 对话框。

public class NoBasicAuthDialogAuthorizeAttribute : AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        base.HandleUnauthorizedRequest(filterContext);
        filterContext.Result = new HttpStatusCodeResult(400);
    }
}

使用如下:

[NoBasicAuthDialogAuthorize(Roles = "A-Team")]
public ActionResult CarType()
{
 // your code goes here
}

希望这可以节省您一些时间。

于 2016-05-24T11:16:30.793 回答
0

我最近在为三星 Tizen 智能电视开发网络应用程序时遇到了类似的情况。需要扫描整个本地网络,但很少有 IP 地址返回带有“www-authenticate”标头的“401 Unauthorized”响应。由于“基本”身份验证类型( https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication),它弹出了一个浏览器身份验证弹出窗口,要求用户输入“用户名”和“密码” 。

为了摆脱这种情况,对我有用的简单方法是设置凭据:“省略” Fetc Api Call ( https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch )。官方文档说:

为了确保浏览器不在请求中包含凭据,请使用凭据:'省略'

fetch('https://example.com', {
  credentials: 'omit'
})

于 2021-01-31T08:42:59.177 回答