如果刷新(JWT)令牌未经授权(401响应),则AngularJS重定向至登录

时间:2019-02-18 19:20:14

标签: angularjs jwt refresh-token csrf-token

如果刷新(jwt)令牌未经授权(在第一个令牌过期后),我将用户重定向到登录页面时遇到问题。有两种未授权令牌的情况;

  • 第一个:当jwt令牌基于401响应而过期时,将调用新的刷新服务以通过$ http-interceptors(config)生成新令牌。

  • 2nd:当刷新令牌也获得未经授权的(401)响应时,这是用户应重定向到登录页面的时间。

我能够在第一种情况下发送刷新令牌,并且按预期工作正常,但是如果刷新令牌也得到未经授权的(401)响应,则无法将用户重定向到登录页面。

这是我的代码;

authInterceptor.service.js

angular.module('someApp').factory('AuthorizationTokenService', AuthorizationTokenService);

AuthorizationTokenService.$inject = ['$q', '$injector', '$cookies'];
function AuthorizationTokenService($q, $injector, $cookies) {
  // Local storage for token
  var tokenVM = {
    accessToken: null
  };

  // Subscribed listeners which will get notified when new Access Token is available
  var subscribers = [];

  // Promise for getting new Access Token from backend
  var deferedRefreshAccessToken = null;

  var service = {
    getLocalAccessToken: getLocalAccessToken,
    refreshAccessToken: refreshAccessToken,
    isAccessTokenExpired: isAccessTokenExpired,
    subscribe: subscribe
  };

  return service;

  ////////////////////////////////////

  // Get the new Access Token from backend
  function refreshAccessToken() {

    // If already waiting for the Promise, return it.
    if( deferedRefreshAccessToken ) {

      return deferedRefreshAccessToken.promise 

    } else {

      deferedRefreshAccessToken = $q.defer();

      // Get $http service with $injector to avoid circular dependency
      var http = $injector.get('$http');

      http({
        method: 'POST',
        url: 'api_url',
        params: {
          grant_type: 'refresh',
          id_token: $cookies.get('access_token')
        }
      })
        .then(function mySucces(response) {
          var data = response.data;
          if( data ){
            // Save new Access Token
            $cookies.put('access_token', data.access_token);

            if( $cookies.get('access_token') ) {

              // Resolve Promise
              deferedRefreshAccessToken.resolve(data.access_token);

              // Notify all subscribers
              notifySubscribersNewAccessToken(data.access_token);
              deferedRefreshAccessToken = null;
            }
          }
        }, function myError(error) {
          deferedRefreshAccessToken.reject(error);
          deferedRefreshAccessToken = null;
        });

      return deferedRefreshAccessToken.promise;
    } 

  }

  function getLocalAccessToken() {
    // get accesstoken from storage - $cookies
    if ( $cookies.get('access_token') ) {
      var access_token = $cookies.get('access_token')
      return access_token;
    }
  }

  function isAccessTokenExpired() {
    // Check if expiresAt is older then current Date
  }

  function saveToken(accessToken) {
    // get accesstoken from storage - $cookies
    var access_token = $cookies.put('access_token');

    console.log('access_token ' + access_token);

    return access_token;
  }

  // This function will call all listeners (callbacks) and notify them that new access token is available
  // This is used to notify the web socket that new access token is available
  function notifySubscribersNewAccessToken(accessToken) {
    angular.forEach(subscribers, function(subscriber) {
      subscriber(accessToken);
    });
  }

  // Subscribe to this service. Be notifyed when access token is renewed
  function subscribe(callback) {
    subscribers.push(callback);
  }
}

并在Config(app.js)中

config.$inject = ['$stateProvider', '$urlRouterProvider', '$httpProvider'];
function config($stateProvider, $urlRouterProvider, $httpProvider) {

  // Push httpRequestInterceptor
  // $httpProvider.interceptors.push('httpRequestInterceptor');

  //Intercept all http requests
  $httpProvider.interceptors.push(['$injector', '$q', "AuthorizationTokenService", "$cookies", function ($injector, $q, AuthorizationTokenService, $cookies) {
    var cachedRequest = null;

    return {
      request: function (config) {
        //If request if for API attach Authorization header with Access Token
        if (config.url.indexOf("api") != -1) {
          // var accessToken = AuthorizationTokenService.getLocalAccessToken();
          console.log('cookie ' + $cookies.get('access_token'));
          config.headers.Authorization = 'Bearer ' + $cookies.get('access_token');
        }
        return config;
      },
      responseError: function (response) {
        switch (response.status) {
          // Detect if reponse error is 401 (Unauthorized)
          case 401:

          // Cache this request
          var deferred = $q.defer();
          if(!cachedRequest) {
            // Cache request for renewing Access Token and wait for Promise
            cachedRequest = AuthorizationTokenService.refreshAccessToken();
          }

          // When Promise is resolved, new Access Token is returend 
          cachedRequest.then(function(accessToken) {
            cachedRequest = null;
            if (accessToken) {
              // Resend this request when Access Token is renewed
              $injector.get("$http")(response.config).then(function(resp) {
                // Resolve this request (successfully this time)
                deferred.resolve(resp);
              },function(resp) {
                deferred.reject();
                console.log('success: refresh token has expired');
              });
            } else {
              // If any error occurs reject the Promise
              console.log('error: refresh token has expired');
              deferred.reject();
            }
          }, function(response) {
            // If any error occurs reject the Promise
            cachedRequest = null;
            deferred.reject();
            return;
          });

          return deferred.promise;
        }

        // If any error occurs reject the Promise
        return $q.reject(response);
      }
    };
  }]);
}

service config 中,我都尝试实现基于双401重定向用户的方式(平均刷新令牌也已过期并以401响应)。

我还尝试了多个后代401条件,但效果不佳。 (下面的示例)

responseError: function (response) {
  // Detect if reponse error is 401 (Unauthorized)
  if (response.status === 401) {

    // Cache this request
    var deferred = $q.defer();
    if(!cachedRequest) {
      // Cache request for renewing Access Token and wait for Promise
      cachedRequest = AuthorizationTokenService.refreshAccessToken();
    }

    // When Promise is resolved, new Access Token is returend 
    cachedRequest.then(function(accessToken) {
      cachedRequest = null;
      if (response.status === 401) {
        console.log('refresh token also expired');
        $location.path('/login');
      } else {
        // Resend this request when Access Token is renewed
        $injector.get("$http")(response.config).then(function(resp) {
          // Resolve this request (successfully this time)
          deferred.resolve(resp);
        },function(resp) {
          deferred.reject();
          console.log('success: refresh token has expired');
        });
      }
    }, function(response) {
      // If any error occurs reject the Promise
      cachedRequest = null;
      deferred.reject();
      return;
    });

    return deferred.promise;
  }
}

基于上述代码,请指导我我做错了什么,或者登录/实施可能有问题。无论哪种情况都请帮我。谢谢

1 个答案:

答案 0 :(得分:0)

我设法通过下面的代码行简单地解决了这个问题;

配置(app.js)

// Cache this request
var deferred = $q.defer();
if(!cachedRequest) {
    // Cache request for renewing Access Token and wait for Promise
    cachedRequest = AuthorizationTokenService.refreshAccessToken();
} else {
    // this is where it checks for request token expiry
    do_logout();
}