2014-10-24 11 views
20

Ho un'app angolare che a volte richiede più $ http.get richieste per stato. L'app utilizza JWT per l'autenticazione utente con token di aggiornamento. Il server API invia 401 per ogni richiesta non riuscita a causa di errore di autorizzazione. Ho creato un http interceptor che richiede un nuovo token con il token di aggiornamento sugli errori 401 e dopo di ciò viene nuovamente inviata la richiesta originale.AngularJS - intercettore http - invia di nuovo tutte le richieste dopo l'aggiornamento del token

Il problema è che se uno stato effettua ad esempio 2 richieste $ http.get ed entrambi ottengono una risposta 401, allora rinnovo il token di accesso due volte. Ovviamente voglio solo aggiornare il token una volta, ma voglio comunque inviare nuovamente le richieste non riuscite.

È possibile e se sì, come?

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

puoi postare lo snippet di codice del tuo intercettore e come rinviare le richieste? –

+0

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

+1

@KevinHakanson: Nemmeno io ottengo quella lib. L'evento 'auth-loginRequired' sparerebbe ancora due volte, facendo in modo che l'app aggiorni il token due volte. Ho sbagliato? Cosa mi manca? – Andrew

risposta

38

L'intercettore deve tenere traccia della presenza o meno di una richiesta di autenticazione "in volo". Può farlo mantenendo un riferimento alla promessa restituita dalla richiesta di autenticazione. Se c'è una richiesta in volo e ottieni un altro 401, usa la promessa memorizzata nella cache invece di iniziare una nuova richiesta.

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

Questa soluzione è ottima poiché restituisce tutte le richieste al controller, dove vengono interpretate dalle funzioni di successo e di errore. Grazie mille per questa soluzione! – Klapsa2503

+0

grazie per la tua risposta, saluti. –

+0

funziona esattamente come mi serviva. grazie – Anton

Problemi correlati