使用 AngularJS 拦截器阻止 HTTP 基本身份验证对话框
Posted
技术标签:
【中文标题】使用 AngularJS 拦截器阻止 HTTP 基本身份验证对话框【英文标题】:Preventing HTTP Basic Auth Dialog using AngularJS Interceptors 【发布时间】:2014-07-30 14:02:59 【问题描述】:我正在使用 RESTful API 构建 AngularJS (1.2.16) Web 应用程序,并且我想针对身份验证信息无效或不存在的请求发送 401 Unauthorized 响应。当我这样做时,即使存在 HTTP 拦截器,当通过 AngularJS 发出 AJAX 请求时,我也会看到浏览器显示的基本“需要身份验证”对话框。我的拦截器在该对话框之后运行,这为时已晚,无法做一些有用的事情。
一个具体的例子:
除非存在授权令牌,否则我的后端 API 会为 /api/things
返回 401。漂亮又简单。
在 AngularJS 应用程序方面,我查看了 docs 并在 config
块中设置了这样的拦截器:
$httpProvider.interceptors.push(['$q', function ($q)
return
'responseError': function (rejection)
if (rejection.status === 401)
console.log('Got a 401')
return $q.reject(rejection)
])
当我加载我的应用程序,删除身份验证令牌,并对 /api/things
执行 AJAX 调用(希望触发上述拦截器)时,我看到了:
如果我取消该对话框,我会看到“Got a 401”的console.log
输出,这是我希望看到的而不是那个对话框:
很明显,拦截器在工作,但是拦截太晚了!
我在网上看到很多关于在这种情况下使用 AngularJS 进行身份验证的帖子,它们似乎都使用了 HTTP 拦截器,但没有一个提到弹出的基本身份验证对话框。我对其外观的一些错误想法包括:
响应中缺少Content-Type: application/json
标头?不,它就在那里。
需要返回承诺拒绝以外的其他内容?无论返回什么,该代码始终在对话框之后运行。
我是否遗漏了一些设置步骤或错误地使用了拦截器?
【问题讨论】:
为什么不使用 403 而不是 401? 我使用了 401,因为在最初的问题中,我删除了身份验证令牌(在我的情况下是 cookie)。 401 是在这里使用的正确响应代码,因为该请求需要身份验证并且没有提供。 403 用于提供身份验证但服务器拒绝访问资源时。 【参考方案1】:想通了!
诀窍是发送一个WWW-Authenticate
响应标头,它的值不是Basic
。然后,您可以使用基本的 $http
拦截器或更聪明的东西(例如 angular-http-auth)来捕获 401。
【讨论】:
您能详细说明一下吗? “像 angular-http-auth 这样更聪明的东西”要求服务器发送 200 响应。我正在寻找服务器返回 401 代码的解决方案。 我认为它只在'Authorization'时弹出:'Basic XXX' 请发布一些代码示例,我遇到了同样的问题。 ios 基本身份验证请求永远不会完成 有没有例子可以使用这个angular-http-auth并拦截响应来修改请求头?【参考方案2】:我在 Spring Boot Security (HTTP basic) 中遇到了这个问题,从 Angular 1.3 开始,您必须设置 $httpProvider.defaults.headers.common["X-Requested-With"] = 'XMLHttpRequest';
才能不出现弹出窗口。
【讨论】:
它不适用于我使用 Angular 1.5 和 firefox 48.x 的应用程序...即使使用 X-Requested-With 标头也会触发基本身份验证提示... 对我来说也一样。我已将此标头添加到邮递员中。但它不起作用。 这解决了我的问题。应用您的问题后,浏览器身份验证模式被隐藏。谢谢。 这个想法对我有用,虽然我创建了一个拦截器,并且仅在请求 URL 用于我的 REST API 时才添加此标头。【参考方案3】:供将来参考
我在尝试处理401
错误时提出了这个解决方案。
我没有选项将Basic
重写为x-Basic
或类似的东西,所以我决定在客户端使用 Angular 来处理它。
启动注销时,首先尝试向假用户发出错误请求以丢弃当前缓存的凭据。
我有这个函数来处理请求(它使用 jquery 的 $.ajax
并禁用异步调用):
function authenticateUser(username, hash)
var result = false;
var encoded = btoa(username + ':' + hash);
$.ajax(
type: "POST",
beforeSend: function (request)
request.setRequestHeader("Authorization", 'Basic ' + encoded);
,
url: "user/current",
statusCode:
401: function ()
result = false;
,
200: function (response)
result = response;
,
async: false
);
return result;
所以当我尝试注销用户时,会发生这种情况:
//This will send a request with a non-existant user.
//The purpose is to overwrite the cached data with something else
accountServices.authenticateUser('logout','logout');
//Since setting headers.common.Authorization = '' will still send some
//kind of auth data, I've redefined the headers.common object to get
//rid of the Authorization property
$http.defaults.headers.common = Accept: "application/json, text/plain, */*";
【讨论】:
以上是关于使用 AngularJS 拦截器阻止 HTTP 基本身份验证对话框的主要内容,如果未能解决你的问题,请参考以下文章
angularjs拦截器:为啥在$http拦截器中没有收到正确的状态码和消息