Предотвращение HTTP Basic Auth Dialog с использованием AngularJS Interceptors
Я создаю веб-приложение AngularJS (1.2.16) с API RESTful, и я бы хотел отправить 401 Несанкционированные ответы на запросы, в которых данные аутентификации являются недействительными или нет. Когда я это делаю, даже с присутствующим HTTP-перехватчиком, я вижу представленный браузером базовый диалог "Требуется проверка подлинности", когда запрос AJAX выполняется через AngularJS. Мой перехватчик запускается после этого диалога, что слишком поздно, чтобы сделать что-то полезное.
Конкретный пример:
My backend API возвращает 401 для /api/things
, если нет токена авторизации. Приятно и просто.
На стороне приложения 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)
}
}
}])
Когда я загружаю свое приложение, удаляю токен аутентификации и выполняю вызов AJAX /api/things
(чтобы, надеюсь, запустить вышеупомянутый перехватчик), я вижу следующее:
![Authentication Required]()
Если я отменил это диалоговое окно, я вижу вывод console.log
"Got a 401", который я надеялся увидеть вместо этого диалога:
![Got a 401]()
Ясно, что перехватчик работает, но перехватывает слишком поздно!
Я вижу многочисленные сообщения в Интернете об аутентификации с помощью AngularJS в ситуациях, подобных этому, и все они, похоже, используют HTTP-перехватчики, но ни один из них не упоминает об открытии основного диалога auth. Некоторые ошибочные мысли, которые я имел для его появления, включали:
- Отсутствует заголовок
Content-Type: application/json
в ответе? Нет, это там.
- Нужно вернуть что-то другое, кроме обещания отклонения? Этот код всегда запускается после диалога, независимо от того, что возвращается.
Я пропустил какой-то шаг установки или неправильно использовал перехватчик?
Ответы
Ответ 1
Выяснилось!
Трюк состоял в том, чтобы отправить заголовок ответа WWW-Authenticate
некоторого значения, отличного от Basic
. Затем вы можете захватить 401 с помощью базового перехватчика $http
или что-то еще более умное, например angular-http-auth.
Ответ 2
У меня была эта проблема вместе с Spring Boot Security (HTTP basic), а с Angular 1.3 вам нужно установить $httpProvider.defaults.headers.common["X-Requested-With"] = 'XMLHttpRequest';
, чтобы всплывающее окно не отображалось.
Ответ 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, */*"};