我已经实现了身份验证系统,从角度1.0.8升级到1.2.x后, 系统不像以前那样工作。当用户登录时获取令牌。令牌过期时 调用新令牌的刷新功能。在服务器上成功创建了新令牌,它是 存储到数据库。但是客户端没有获得这个新令牌,所以它再次请求一个新令牌, 并一次又一次地退出。服务器端(MVC Web Api)工作正常,所以问题必须 在客户端。问题必须在重试队列上。下面我贴了相关的代码和 两个版本的应用程序(1.0.8和1.2.x)的控制台跟踪。 我现在正在努力奋斗这几天,我无法弄明白。
在下面的链接中,有5个相关的代码块:
代码:http://pastebin.com/Jy2mzLgj
应用程序的控制台跟踪角度1.0.8:http://pastebin.com/aL0VkwdN
和角度1.2.x:http://pastebin.com/WFEuC6WB
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');
}]);
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;
}]);
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;
}]);
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;
}
非常感谢, 贾尼
答案 0 :(得分:2)
拦截器应该总是回复承诺。
因此,在responseError中,您应该更好return $q.reject(originalResponse);
而不仅仅是return originalResponse
。
希望这有帮助
答案 1 :(得分:2)
您是否尝试修复1.2日志中的错误?
错误:[ngRepeat:dupes]不允许在转发器中重复。使用'track by'表达式指定唯一键。 Repeater:在client.projects中的项目,Duplicate键:string:e
该错误正好在您需要查看$ httpHeaders设置行的位置。看起来你的session.tokenrefresh不起作用(而且这个代码也从pastebin中丢失,所以我无法检查。)
答案 2 :(得分:0)
我认为你的拦截器在errorResponse方法中返回错误的结果。
出于同样的原因,我遇到了一些"undebuggable" issue。 使用此代码,您可能会遇到一些无限循环流... 希望这会有所帮助。