2015-11-21 31 views
0

使用JWT進行API授權的Angular應用程序在調用API返回401 "Unauthorized"時啓動登錄對話框,以允許用戶輸入其憑據並獲取新的有效JWT。然後,應用程序重試失敗的未授權請求並保持流動。在刷新授權令牌的同時處理多個未經授權的請求

此處列出的代碼基於克里斯克拉克的this solution

.config(['$httpProvider', function($httpProvider) { 
$httpProvider.interceptors.push(['$q', '$location', '$injector', function ($q, $location, $injector) { 
    return { 
     'responseError': function(response) { 
      // Keep the response waiting until we get new JWT 
      var deferred = $q.defer(); 
      if (response.status === 401 && response.data.error && response.data.error.message.toLowerCase() === "unauthorized") { 
       // JWT has expired 
       // Open login dialog 
       var cslAuth = $injector.get('cslAuth'); 
       if (cslAuth.isLoggedIn()) { 
        // Logout user, next pending request will not trigger auth dialog 
        cslAuth.logout(); 
        $injector.get('ngDialog').openConfirm({ 
         template: 'web_app/views/login.html', 
         className: 'ngdialog-theme-default', 
         showClose: false, 
         controller: 'LoginCtrl', 
         cache: false 
        }) 
        .then(
         function(value) { 
          // JWT has been refreshed. Try pending request again 
          var config = response.config; 
          // Inject the new token in the Auth header 
          config.headers.Authentication = cslAuth.getTokenHeader(); 
          $injector.get("$http")(config).then(
           function(response){ 
            deferred.resolve(response); 
           }, 
           function(response) { 
            deferred.reject(); 
           } 
          ); 
         }, 
         function(value) { 
          deferred.reject(); 
         } 
        ); 
       } 
      } else { 
       return $q.reject(response); 
      } 
      // Return a promise while waiting for auth refresh 
      return deferred.promise; 
     } 
    } 
}]) 
}]) 

問題是當有多個請求與過期令牌一起進行時。第一個回來應該觸發登錄對話框並獲得新的令牌。但是如何讓其他待處理的請求等待,直到新令牌可用? 可以設置一個標誌來告訴所有後續傳入的響應是否有新的令牌被請求。可以返回一個承諾,並且所有配置對象都可以存儲在一個服務中的數組中。當新令牌可用時,所有等待請求都可以重試。但是在新的令牌可用之後未授權的請求返回會發生什麼?他們將觸發一個新的登錄對話框。

的一些注意事項的演員:

  • This answer給出瞭解決方案的相關問題,但因爲是這裏涉及一個新的登錄,我不能看到如何解決這種情況下適應。

  • 這不是自動更新令牌的選項。令牌將有8小時到期(工作會話),並且新登錄是強制性的。

  • 在配置對象中注入服務(cslAuth$http)安全嗎?我的代碼正在工作,但我已經閱讀,他們不能完全準備好在這一點上。

回答

0

此代碼提高了一個在2種方法中的問題貼:

  • 保持pendingRequests的陣列,以重試然後succefull令牌之後刷新
  • 在401,它檢測開關如果所使用的身份驗證令牌與當前的不同,在不詢問登錄的情況下再次重試請求。這解決了未包含在pendingRequests數組中的請求的問題。

代碼:

/ HTTP Interceptors 
.config(['$httpProvider', function($httpProvider) { 
    $httpProvider.interceptors.push(['$q', '$location', '$injector', function ($q, $location, $injector) { 
     var pendingRequests = []; 
     function retryRequest(deferred, config, cslAuth) { 
      config.headers.Authentication = cslAuth.getTokenHeader(); 
      $injector.get("$http")(config).then(
       function(response){ 
        deferred.resolve(response); 
       }, 
       function(response) { 
        deferred.reject(); 
       } 
      ); 
     } 
     return { 
      'responseError': function(response) { 
       switch (response.status) { 
        case 401: // JWT has expired 
         // To keep the response waiting until we get new JWT 
         var deferred = $q.defer(); 
         var cslAuth = $injector.get('cslAuth'); 
         // Check if a new token exists. Then retry the request with new token 
         if (response.config.headers.Authentication != cslAuth.getTokenHeader()) { 
          retryRequest(deferred, response.config, cslAuth); 
          // Return a promise while waiting 
          return deferred.promise; 
         } 
         // Open login dialog 
         if (cslAuth.isLoggedIn()) { 
          // Logout user, next pending request will not trigger auth dialog 
          cslAuth.logout(); 
          $injector.get('ngDialog').openConfirm({ 
           template: 'web_app/views/login-inner.html', 
           className: 'ngdialog-theme-default', 
           showClose: false, 
           controller: 'LoginCtrl' 
          }) 
          .then(
           function(value) { 
            // JWT has been refreshed. Try pending requests again 
            for (var i = 0; i < pendingRequests.length; i++) { 
             retryRequest(pendingRequests[i].deferred, pendingRequests[i].config, cslAuth); 
            } 
           }, 
           function(value) { 
            pendingRequests[i].deferred.reject(); 
           } 
          ); 
         } 
         // Return a promise while waiting for auth refresh 
         pendingRequests.push({'deferred': deferred, 'config': response.config}); 
         return deferred.promise; 
         break; 
        default: // What happened? 
         return $q.reject(response); 
         break; 
       } 
      } 
     } 
    }]) 
}])