2014-10-24 10 views
20

Ich habe eine eckige App, die manchmal mehrere $ http.get Anfragen pro Zustand. Die App verwendet JWT zur Benutzerauthentifizierung mit Aktualisierungstoken. Der API-Server sendet 401 bei jeder Anfrage, die wegen eines Authentifizierungsfehlers fehlgeschlagen ist. Ich habe eine http interceptor gemacht, die ein neues Token mit dem Refresh-Token auf 401 Fehler anfordert und danach die ursprüngliche Anfrage erneut sendet.AngularJS - http Interceptor - alle Anfrage nach Token Refresh erneut senden

Das Problem ist, wenn ein Zustand zum Beispiel 2 $ http.get Anfragen macht und beide 401 Antwort bekommen, dann erneuere ich das Zugriffs-Token zweimal. Offensichtlich möchte ich das Token nur einmal aktualisieren, ABER ich möchte immer noch BEIDE gescheiterte Anfragen erneut senden.

Ist das erreichbar und wenn ja wie?

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

können Sie das Code-Snippet Ihres Interceptors posten und wie Sie die Anfragen erneut senden? –

+0

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

+1

@KevinHakanson: Ich verstehe das auch nicht. Das 'event: auth-loginRequired' würde immer noch zweimal ausgelöst, wodurch die App das Token zweimal aktualisiert. Liege ich falsch? Was vermisse ich? – Andrew

Antwort

38

Ihr Interceptor muss im Auge behalten, ob er eine Authentifizierungsanforderung "im Flug" hat oder nicht. Sie können dies tun, indem Sie einen Verweis auf die von der Authentifizierungsanforderung zurückgegebene Zusage beibehalten. Wenn es eine Anfrage im Flug gibt und Sie eine weitere 401 erhalten, verwenden Sie einfach das zwischengespeicherte Versprechen, anstatt eine neue Anfrage zu starten.

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

Diese Lösung ist großartig, da sie alle Anfragen zurück an die Steuerung sendet, wo sie von Erfolgs- und Fehlerfunktionen interpretiert werden. Vielen Dank für diese Lösung! – Klapsa2503

+0

danke für deine antwort, grüße. –

+0

funktioniert genau so, wie ich es brauchte. danke – Anton

Verwandte Themen