2017-04-24 83 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

是!谢谢你的提示。根据规则我改变了我的答案。 –