2017-04-24 77 views
2

我已經撰寫了一個用Angular編寫的SPA,並且正在使用基於Azure AD令牌的身份驗證以及通過持票人令牌保護的資源(如API)。這使得SPA能夠從Azure中檢索續訂令牌,並永遠保持登錄狀態,據我所知。ADAL JS強制註銷SPA

我使用ADAL JavaScript庫的角度來做到這一點:

https://github.com/AzureAD/azure-activedirectory-library-for-js

現在有限制的用戶會話的時間預定量的要求,可以說15小時。

我寫了一個不錯的小服務,它在登錄時存儲cookie,每次請求它檢查cookie中的日期以及它們是否超出其最大會話限制。如果用戶持續使用應用程序或關閉瀏覽器,此功能可以正常工作 - 但如果他們將瀏覽器保持打開狀態,令牌只會在後臺更新,並且會保持登錄狀態。

我正在嘗試解決此問題使用無聲登出解決方案。這意味着,我希望用戶在會話過期後被強制進入安全登錄頁面。

這似乎是一個常見的情況,但我似乎無法弄清楚如何實現它,因爲ADAL在後臺使用iFrame。我想過使用計時器/時間間隔,但似乎很麻煩。

只是FYI我按照下面的代碼示例使用adalAuthenticationService.logout()。我也嘗試清除會話緩存,這會起作用 - 但ADAL會默默刷新令牌。我也嘗試將redirectUri位置設置爲未經過身份驗證的頁面,但仍然只會在用戶採取措施時重定向到那裏。如果瀏覽器保持打開狀態,令牌將自行重置。

var maxTime = 15; // hours allowed in session 

    // event to fire check; maybe this can be different, and is my problem? 
    $rootScope.$on('$viewContentLoaded', function() { 
     $scope.checkLogoutCookie(); 
    }); 
    $scope.logout = function() { 
     adalAuthenticationService.logout(); 
    }; 
    function setCookie(c) {} // implementation details don't matter.... 
    function getCookie(c) {} // implementation details don't matter.... 
    $scope.checkLogoutCookie = function() { 
     var lastLogin = getCookie("lastLogin"); 
     var loginDate = new Date(); 
     if (lastLogin === "") { // is empty 
      setCookie("lastLogin", loginDate, 365); 
     } else { 
      var lastDate = new Date(lastLogin); 
      var hours = Math.abs(lastDate - loginDate)/36e5; 
      if (hours > maxTime) { 
       setCookie("lastLogin", "", 0); 
       $scope.logout(); 
      } 
     } 
    } 

回答

3

我寫了一個可愛的小服務,存儲在登錄時cookie中,每一個請求它檢查cookie中的日期,如果他們超過了其最大會話限制。這工作正常,如果用戶持續使用的應用程序,或關閉瀏覽器 - 但如果他們離開自己的瀏覽器中打開,令牌只會在後臺更新和他們處於登錄狀態

根據描述,似乎代碼檢查日期是在adal-angular.js的HTTP請求攔截器之後執行的。

如果可能,你需要在ADAL庫的攔截器之前實現這個函數。

如果不可能,您可以將業務邏輯更改爲 檢查應用程序會話是否在獲取令牌之前未過期。爲此,我們需要更改JavaScript的Active Directory認證庫(ADAL)的源代碼。

例如,您可以更改的HTTP請求攔截器adal-angular.js用於插入代碼以檢查您的應用是否在會話中。這裏是攔截器,供您參考代碼:

AdalModule.factory('ProtectedResourceInterceptor', ['adalAuthenticationService', '$q', '$rootScope', '$templateCache', function (authService, $q, $rootScope, $templateCache) { 

    return { 
     request: function (config) { 
      if (config) { 

       config.headers = config.headers || {}; 

       // if the request can be served via templateCache, no need to token 
       if ($templateCache.get(config.url)) return config; 

       var resource = authService.getResourceForEndpoint(config.url); 
       authService.verbose('Url: ' + config.url + ' maps to resource: ' + resource); 
       if (resource === null) { 
        return config; 
       } 
//add/modify the code here 
       var tokenStored = authService.getCachedToken(resource); 
       if (tokenStored) { 
        authService.info('Token is available for this url ' + config.url); 
        // check endpoint mapping if provided 
        config.headers.Authorization = 'Bearer ' + tokenStored; 
        return config; 
       } 
       else { 
        // Cancel request if login is starting 
        if (authService.loginInProgress()) { 
         if (authService.config.popUp) { 
          authService.info('Url: ' + config.url + ' will be loaded after login is successful'); 
          var delayedRequest = $q.defer(); 
          $rootScope.$on('adal:loginSuccess', function (event, token) { 
           if (token) { 
            authService.info('Login completed, sending request for ' + config.url); 
            config.headers.Authorization = 'Bearer ' + tokenStored; 
            delayedRequest.resolve(config); 
           } 
          }); 
          return delayedRequest.promise; 
         } 
         else { 
          authService.info('login is in progress.'); 
          config.data = 'login in progress, cancelling the request for ' + config.url; 
          return $q.reject(config); 
         } 
        } 
        else { 
         // delayed request to return after iframe completes 
         var delayedRequest = $q.defer(); 
         authService.acquireToken(resource).then(function (token) { 
          authService.verbose('Token is available'); 
          config.headers.Authorization = 'Bearer ' + token; 
          delayedRequest.resolve(config); 
         }, function (error) { 
          config.data = error; 
          delayedRequest.reject(config); 
         }); 

         return delayedRequest.promise; 
        } 
       } 
      } 
     }, 
     responseError: function (rejection) { 
      authService.info('Getting error in the response: ' + JSON.stringify(rejection)); 
      if (rejection) { 
       if (rejection.status === 401) { 
        var resource = authService.getResourceForEndpoint(rejection.config.url); 
        authService.clearCacheForResource(resource); 
        $rootScope.$broadcast('adal:notAuthorized', rejection, resource); 
       } 
       else { 
        $rootScope.$broadcast('adal:errorResponse', rejection); 
       } 
       return $q.reject(rejection); 
      } 
     } 
    }; 
}]); 
1

看那adalAuthenticationService的方法logOut()。我認爲問題在於您錯誤地使用了logout()。請注意0​​方法中的o的大寫字母。

+0

這不提供問題的答案。一旦你有足夠的[聲譽](https://stackoverflow.com/help/whats-reputation),你將可以[對任何帖子發表評論](https://stackoverflow.com/help/privileges/comment);相反,[提供不需要提問者澄清的答案](https://meta.stackexchange.com/questions/214173/why-do-i-need-50-reputation-to-comment-what-c​​an- I-DO-代替)。 - [來自評論](/ review/low-quality-posts/17680085) –

+0

是!謝謝你的提示。根據規則我改變了我的答案。 –