Предотвращение диалога 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", который я надеялся увидеть вместо этого диалога:

Получил 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, */*"};
Другие вопросы по тегам