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