2014-10-24 2 views
20

У меня есть угловое приложение, которое иногда выполняет несколько запросов $ http.get для каждого состояния. Приложение использует JWT для авторизации пользователя с обновляющими токенами. Сервер API отправляет 401 при каждом неудавшемся запросе из-за ошибки auth. Я сделал http interceptor, который запрашивает новый токен с токеном обновления при ошибках 401 и после этого отправляет исходный запрос.AngularJS - http interceptor - повторно отправить весь запрос после обновления токена

Проблема в том, что если состояние делает, например, 2 $ http.get-запросы, и оба получают ответ 401, я дважды обновляю токен доступа. Очевидно, что я хочу только один раз обновить токен, но я все еще хочу повторно отправить BOTH неудачные запросы.

Является ли это достижимым, и если да, то каким образом?

app.factory('AuthInterceptor', function($q, $injector, RESOURCE_URL, API_BASE, authService) { 
    return { 
     request: function(config) { 
      config.headers = config.headers || {}; 
      if (authService.getAccessToken()) { 
       if (config.url.substring(0, RESOURCE_URL.length) !== RESOURCE_URL) { 
        config.headers.Authorization = 'Bearer ' + authService.getAccessToken(); 
       } 
      } 
      return config; 
     }, 
     responseError: function(response) { 
      switch (response.status) { 
       case 401: 
        var deferred = $q.defer(); 
        $injector.get("$http").post(API_BASE + '/api/auth/refresh', {refreshtoken: authService.getRefreshToken()}).then(function(r) { 
         if (r.data.data.accesstoken && r.data.data.refreshtoken && r.data.data.expiresin) { 
          authService.setAccessToken(r.data.data.accesstoken); 
          authService.setRefreshToken(r.data.data.refreshtoken); 
          authService.setExpiresIn(r.data.data.expiresin); 
          $injector.get("$http")(response.config).then(function(resp) { 
           deferred.resolve(resp); 
          },function(resp) { 
           deferred.reject(); 
          }); 
         } else { 
          deferred.reject(); 
         } 
        }, function(response) { 
         deferred.reject(); 
         authService.clear(); 
         $injector.get("$state").go('guest.login'); 
         return; 
        }); 
        return deferred.promise; 
        break; 
       default: 
        authService.clear(); 
        $injector.get("$state").go('guest.login'); 
        break; 
      } 
      return response || $q.when(response); 
     } 
    }; 
}); 
+1

Вы можете разместить фрагмент кода вашего перехватчика и как отправить запрос повторно? –

+0

делает http://stackoverflow.com/questions/18638211/how-can-i-send-request-again-in-response-interceptor help answer? –

+1

@KevinHakanson: Я тоже не получаю эту библиотеку. «Событие: auth-loginRequired» все равно срабатывает дважды, чтобы приложение обновляло токен дважды. Я ошибаюсь? Что мне не хватает? – Andrew

ответ

38

Ваш перехватчик должен отслеживать, имеет ли он запрос на аутентификацию «в полете». Он может сделать это, сохранив ссылку на обещание, возвращенное запросом аутентификации. Если в полете есть запрос, и вы получаете еще 401, просто используйте это обещание с кешем вместо того, чтобы инициировать новый запрос.

app.factory('AuthInterceptor', function($q, $injector, RESOURCE_URL, API_BASE, authService) { 
    var inFlightAuthRequest = null; 
    return { 
     request: function(config) { 
      config.headers = config.headers || {}; 
      if (authService.getAccessToken()) { 
       if (config.url.substring(0, RESOURCE_URL.length) !== RESOURCE_URL) { 
        config.headers.Authorization = 'Bearer ' + authService.getAccessToken(); 
       } 
      } 
      return config; 
     }, 
     responseError: function(response) { 
      switch (response.status) { 
       case 401: 
        var deferred = $q.defer(); 
        if(!inFlightAuthRequest) { 
         inflightAuthRequest = $injector.get("$http").post(API_BASE + '/api/auth/refresh', {refreshtoken: authService.getRefreshToken()}); 
        } 
        inflightAuthRequest.then(function(r) { 
         inflightAuthRequest = null; 
         if (r.data.data.accesstoken && r.data.data.refreshtoken && r.data.data.expiresin) { 
          authService.setAccessToken(r.data.data.accesstoken); 
          authService.setRefreshToken(r.data.data.refreshtoken); 
          authService.setExpiresIn(r.data.data.expiresin); 
          $injector.get("$http")(response.config).then(function(resp) { 
           deferred.resolve(resp); 
          },function(resp) { 
           deferred.reject(); 
          }); 
         } else { 
          deferred.reject(); 
         } 
        }, function(response) { 
         inflightAuthRequest = null; 
         deferred.reject(); 
         authService.clear(); 
         $injector.get("$state").go('guest.login'); 
         return; 
        }); 
        return deferred.promise; 
        break; 
       default: 
        authService.clear(); 
        $injector.get("$state").go('guest.login'); 
        break; 
      } 
      return response || $q.when(response); 
     } 
    }; 
}); 
+0

Это решение замечательно, так как оно возвращает все запросы обратно контроллеру, где они интерпретируются с успехом и функциями ошибок. Большое спасибо за это решение! – Klapsa2503

+0

спасибо за ваш ответ, привет. –

+0

работает именно так, как мне нужно. спасибо – Anton

Смежные вопросы