AngularJS-http拦截器-令牌刷新后重新发送所有请求
我有一个angular应用程序,它有时会在每个州执行多个$http.get请求。应用程序使用JWT进行用户身份验证,并使用刷新令牌。API服务器在每个由于身份验证错误而失败的请求上发送AngularJS-http拦截器-令牌刷新后重新发送所有请求,angularjs,Angularjs,我有一个angular应用程序,它有时会在每个州执行多个$http.get请求。应用程序使用JWT进行用户身份验证,并使用刷新令牌。API服务器在每个由于身份验证错误而失败的请求上发送401。 我制作了一个http拦截器,它在401错误时请求一个带有刷新令牌的新令牌,然后重新发送原始请求 问题是,如果一个状态发出例如2$http.get请求,并且两个请求都得到401响应,那么我会将访问令牌续订两次。显然,我只想刷新令牌一次,但我仍然想重新发送两个失败的请求 这是否可以实现?如果可以,如何实现 a
401
。
我制作了一个http拦截器
,它在401错误时请求一个带有刷新令牌的新令牌,然后重新发送原始请求
问题是,如果一个状态发出例如2$http.get请求,并且两个请求都得到401响应,那么我会将访问令牌续订两次。显然,我只想刷新令牌一次,但我仍然想重新发送两个失败的请求
这是否可以实现?如果可以,如何实现
app.factory('AuthInterceptor', function($q, $injector, RESOURCE_URL, API_BASE, authService) {
return {
request: function(config) {
config.headers = config.headers || {};
if (authService.getAccessToken()) {
if (config.url.substring(0, RESOURCE_URL.length) !== RESOURCE_URL) {
config.headers.Authorization = 'Bearer ' + authService.getAccessToken();
}
}
return config;
},
responseError: function(response) {
switch (response.status) {
case 401:
var deferred = $q.defer();
$injector.get("$http").post(API_BASE + '/api/auth/refresh', {refreshtoken: authService.getRefreshToken()}).then(function(r) {
if (r.data.data.accesstoken && r.data.data.refreshtoken && r.data.data.expiresin) {
authService.setAccessToken(r.data.data.accesstoken);
authService.setRefreshToken(r.data.data.refreshtoken);
authService.setExpiresIn(r.data.data.expiresin);
$injector.get("$http")(response.config).then(function(resp) {
deferred.resolve(resp);
},function(resp) {
deferred.reject();
});
} else {
deferred.reject();
}
}, function(response) {
deferred.reject();
authService.clear();
$injector.get("$state").go('guest.login');
return;
});
return deferred.promise;
break;
default:
authService.clear();
$injector.get("$state").go('guest.login');
break;
}
return response || $q.when(response);
}
};
});
您的拦截器需要跟踪是否有“正在运行”的身份验证请求。它可以通过保留对身份验证请求返回的承诺的引用来实现这一点。如果有一个请求正在进行中,而您又得到了另一个401,只需使用缓存的承诺,而不是启动一个新的请求。此外,您应该考虑添加逻辑来解释“/API/AUTH/刷新”本身返回401。< /P>的情况。
app.factory('AuthInterceptor', function($q, $injector, RESOURCE_URL, API_BASE, authService) {
var inflightAuthRequest = null;
return {
request: function(config) {
config.headers = config.headers || {};
if (authService.getAccessToken()) {
if (config.url.substring(0, RESOURCE_URL.length) !== RESOURCE_URL) {
config.headers.Authorization = 'Bearer ' + authService.getAccessToken();
}
}
return config;
},
responseError: function(response) {
switch (response.status) {
case 401:
var deferred = $q.defer();
if(!inflightAuthRequest) {
inflightAuthRequest = $injector.get("$http").post(API_BASE + '/api/auth/refresh', {refreshtoken: authService.getRefreshToken()});
}
inflightAuthRequest.then(function(r) {
inflightAuthRequest = null;
if (r.data.data.accesstoken && r.data.data.refreshtoken && r.data.data.expiresin) {
authService.setAccessToken(r.data.data.accesstoken);
authService.setRefreshToken(r.data.data.refreshtoken);
authService.setExpiresIn(r.data.data.expiresin);
$injector.get("$http")(response.config).then(function(resp) {
deferred.resolve(resp);
},function(resp) {
deferred.reject();
});
} else {
deferred.reject();
}
}, function(response) {
inflightAuthRequest = null;
deferred.reject();
authService.clear();
$injector.get("$state").go('guest.login');
return;
});
return deferred.promise;
break;
default:
authService.clear();
$injector.get("$state").go('guest.login');
break;
}
return response || $q.when(response);
}
};
});
乔·恩兹明格的解决方案很好。但是我对回调有一些问题,因为它没有执行。 然后我注意到inflightAuthRequest/inflightAuthRequest中有一点输入错误 我现在的完整解决方案是:
(function() {
'use strict';
angular.module('app.lib.auth', []);
angular.module('app.lib.auth')
.factory('authService', authService);
angular.module('app.lib.auth')
.factory('AuthInterceptor', AuthInterceptor);
function authService($window) {
return {
getToken: function() {
return $window.localStorage.getItem('JWT');
},
getRefreshToken: function() {
return $window.localStorage.getItem('Refresh-JWT');
},
setRefreshToken: function(token) {
$window.localStorage.setItem('Refresh-JWT', token);
},
setToken: function(token) {
$window.localStorage.setItem('JWT', token);
},
clearAllToken: function(){
$window.localStorage.removeItem('JWT');
$window.localStorage.removeItem('Refresh-JWT');
},
clearToken: function(){
$window.localStorage.removeItem('JWT');
},
isLoggedIn: function() {
if ($window.localStorage.getItem('JWT') === null) {
return false;
}
else {
return true;
}
},
toLogin: function(){
$window.location.href = "http://" + $window.location.host + "/tprt/login";
}
}
}
function AuthInterceptor($q, $injector, authService) {
var inFlightAuthRequest = null;
return {
request : function(config) {
config.headers = config.headers || {};
if(authService.getToken()){
config.headers['Authorization'] = authService.getToken();
}
return config;
},
responseError : function(response) {
if(response.config.url == URLS.api_refresh_token){
console.log(JSON.stringify(response));
authService.clearAllToken();
authService.toLogin();
}else{
switch (response.status) {
case 401:
authService.clearToken();
var deferred = $q.defer();
if (!inFlightAuthRequest) {
inFlightAuthRequest = $injector.get("$http").post(
URLS.api_refresh_token, {
refreshtoken : authService.getRefreshToken()
});
}
inFlightAuthRequest.then(function(r) {
inFlightAuthRequest = null;
console.log(JSON.stringify(r));
authService.setToken(r.data.accesstoken);
$injector.get("$http")(response.config).then(function(resp) {
deferred.resolve(resp);
}, function(resp) {
deferred.reject(resp);
});
}, function(error) {
inFlightAuthRequest = null;
deferred.reject();
authService.clearAllToken();
authService.toLogin();
return;
});
return deferred.promise;
break;
default:
return $q.reject(response);
break;
}
return response || $q.when(response);
}
}
}
}
})();
你能发布截取器的代码片段以及如何重新发送请求吗?有帮助吗?@KevinHakanson:我也不知道这个库。
事件:auth login required
仍将触发两次,使应用程序刷新令牌两次。我错了吗?我遗漏了什么?这个解决方案非常棒,因为它将所有请求返回控制器,并在控制器中由成功和错误函数进行解释。非常感谢您提供此解决方案!在访问令牌过期时工作良好,但在刷新令牌过期时出错。有人观察到了同样的行为吗?@SAtahAhmedKhan:它失败了,因为监听器还截获了刷新令牌过期时刷新端点返回的消息。因此,不会执行失败代码。相反,刷新请求在inflightAuthRequest承诺重新执行时排队。要解决这个问题,应该检查response.url。这是一个“安全”的解决方案吗?有权访问控制台(或恶意浏览器插件)的人是否可以通过获取服务并调用authService.getRefreshToken()来访问刷新令牌?@MMK:此代码截获所有401个请求,但如果刷新令牌过期,则所有等待的请求都将失败,但是上面的代码将调度401个refresh失败,以便像所有其他请求一样重新执行。所以,您应该检查response.url以确定401是来自常规请求还是指向令牌刷新端点。若您从令牌刷新端点获得401,那个么您应该将失败返回给所有等待的请求。