javascript - AngularJs - 在等待新 token 时不会跳过请求

标签 javascript angularjs

我已经实现了身份验证系统,并且在从 angular 1.0.8 升级到 1.2.x 之后, 系统不能像以前那样工作。当用户登录时,它会获得一个 token 。当 token 过期时, 调用新 token 的刷新函数。新 token 已在服务器上成功创建,并且是 存入数据库。但是客户端没有得到这个新 token ,所以它再次请求一个新 token , 一次又一次,直到它注销。服务器端(MVC Web Api)工作正常,所以问题一定 在客户端。问题一定在重试队列上。下面我粘贴了相关代码和 两个版本的应用程序(1.0.8 和 1.2.x)的控制台跟踪。 我已经为此苦苦挣扎了好几天,但我无法弄清楚。

在下面的链接中,有5个相关的代码块:

  • interceptor.js(用于拦截请求,两个版本)
  • retryQueue.js(管理重试请求队列)
  • security.js(管理重试队列项的处理程序并从 api 获取新 token )
  • httpHeaders.js(设置标题)
  • tokenHandler.js(处理 cookie 中的 token )

代码:http://pastebin.com/Jy2mzLgj

Angular 1.0.8 中应用程序的控制台跟踪:http://pastebin.com/aL0VkwdN

和 Angular 1.2.x:http://pastebin.com/WFEuC6WB

interceptor.js (angular 1.2.x 版本)

angular.module('security.interceptor', ['security.retryQueue'])
.factory('securityInterceptor', ['$injector', 'securityRetryQueue', '$q',
     function ($injector, queue, $q) {
        return {
            response: function(originalResponse) {
                return originalResponse;
            },
            responseError: function (originalResponse) {
                var exception;
                if (originalResponse.headers){
                    exception = originalResponse.headers('x-eva-api-exception');
                }
                if (originalResponse.status === 401 && 
                   (exception === 'token_not_found' || 
                    exception === 'token_expired')){
                    queue.pushRetryFn(exception, function retryRequest() {
                        return $injector.get('$http')(originalResponse.config);
                    });
                }
                return $q.reject(originalResponse);
            }
        };
     }])
     .config(['$httpProvider', function($httpProvider) {
         $httpProvider.interceptors.push('securityInterceptor');
     }]);

重试队列.js

angular.module('security.retryQueue', [])
.factory('securityRetryQueue', ['$q', '$log', function($q, $log) {
    var retryQueue = [];
var service = {
        onItemAddedCallbacks: [],
        hasMore: function(){
            return retryQueue.length > 0;
        },
        push: function(retryItem){
            retryQueue.push(retryItem);
            angular.forEach(service.onItemAddedCallbacks, function(cb) {
                try {
                    cb(retryItem);
                } 
                catch(e){
                     $log.error('callback threw an error' + e);
                }
            });
        },
        pushRetryFn: function(reason, retryFn){
            if ( arguments.length === 1) {
                retryFn = reason;
                reason = undefined;
            }
            var deferred = $q.defer();
            var retryItem = {
                reason: reason,
                retry: function() {
                    $q.when(retryFn()).then(function(value) {
                        deferred.resolve(value);
                    }, function(value){
                        deferred.reject(value);
                    });
                },
                cancel: function() {
                    deferred.reject();
                }
            };
            service.push(retryItem);
            return deferred.promise;
        },
        retryAll: function() {
            while(service.hasMore()) {
                retryQueue.shift().retry();
            }
        }
    };
    return service;
}]);

安全.js

angular.module('security.service', [
'session.service',
'security.signin',
'security.retryQueue',
'security.tokens',
'ngCookies'
])
.factory('security', ['$location', 'securityRetryQueue', '$q', /* etc. */ function(){
     var skipRequests = false;      
     queue.onItemAddedCallbacks.push(function(retryItem) {
         if (queue.hasMore()) {
             if(skipRequests) {return;}
             skipRequests = true;
             if(retryItem.reason === 'token_expired') {
                 service.refreshToken().then(function(result) {
                     if(result) { queue.retryAll(); }
                     else {service.signout(); }
                     skipRequests = false;
                 });
             } else {
                 skipRequests = false;
                 service.signout();
             }
         }
     });

     var service = {
         showSignin: function() {
             queue.cancelAll();
             redirect('/signin');
         },
         signout: function() {
             if(service.isAuthenticated()){
                 service.currentUser = null;
                 TokenHandler.clear();
                 $cookieStore.remove('current-user');
                 service.showSignin();
             }
         },
         refreshToken: function() {
             var d = $q.defer();
             var token = TokenHandler.getRefreshToken();
             if(!token) { d.resolve(false); }
             var session = new Session({ refreshToken: token });
             session.tokenRefresh(function(result){
                 if(result) { 
                     d.resolve(true); 
                     TokenHandler.set(result);
                 } else {
                     d.resolve(false);
                 }
             });
             return d.promise;
         }
     };

    return service;
}]);

session.service.js

angular.module('session.service', ['ngResource'])

    .factory('Session', ['$resource', '$rootScope', function($resource, $rootScope) {
     var Session = $resource('../api/tokens', {}, {
        create: {method: 'POST'}
    });

    Session.prototype.passwordSignIn = function(ob) {
        return Session.create(angular.extend({
                       grantType: 'password', 
                       clientId: $rootScope.clientId
                }, this), ob);
    };

    Session.prototype.tokenRefresh = function(ob) {
        return Session.create(angular.extend({
                       grantType: 'refresh_token', 
                       clientId: $rootScope.clientId
                    }, this), ob);
    };

    return Session;
}]);

感谢@Zerot 的建议和代码示例,我不得不像这样更改部分拦截器:

if (originalResponse.status === 401 && 
    (exception === 'token_not_found' || exception === 'token_expired')){
    var defer = $q.defer();

    queue.pushRetryFn(exception, function retryRequest() {
            var activeToken = $cookieStore.get('authorization-token').accessToken;
            var config = originalResponse.config;
            config.headers.Authorization = 'Bearer ' + activeToken;
            return $injector.get('$http')(config)
                    .then(function(res) {
                            defer.resolve(res);
                    }, function(err)
                    {
                            defer.reject(err);
                    });
    });

    return defer.promise;
}

非常感谢, 贾尼

最佳答案

您是否尝试修复 1.2 日志中的错误?

Error: [ngRepeat:dupes] Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: project in client.projects, Duplicate key: string:e

该错误恰好出现在您需要查看 $httpHeaders 设置行的位置。看起来你的 session.tokenrefresh 不工作(并且该代码也从 pastebin 中丢失,所以我无法检查。)

关于javascript - AngularJs - 在等待新 token 时不会跳过请求,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23383616/

相关文章:

javascript - AngularJS,工厂,执行 POST 然后 GET 方法

javascript - JS数组声明中的空元素

javascript - ng-repeat 与动态 ng-include 和模板变量范围问题

javascript - 我如何使用 javascript 迭代这个 JSON 对象

javascript - 检测单页应用程序上的应用程序版本更改

javascript - 图像附加不适用于 ng-repeat 中的指令

javascript - Angular - 本地存储不工作

javascript - 密码正则表达式验证?

javascript - youtube 自动播放不工作

javascript - AngularJs ng-repeat 是在复选框上注册两个点击事件吗?