我有一个 angular 应用程序,它有时会为每个状态执行多个 $http.get 请求。该应用程序使用 JWT 进行带有刷新 token 的用户身份验证。 API 服务器发送 401
在由于身份验证错误而失败的每个请求上。
我做了一个 http interceptor
在 401 错误时请求带有刷新 token 的新 token ,然后重新发送原始请求。
问题是,如果一个状态发出例如 2 个 $http.get 请求并且都得到 401 响应,那么我会更新访问 token 两次。显然我只想刷新一次 token ,但我仍然想重新发送两个失败的请求。
这是否可以实现,如果可以,如何实现?
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);
}
};
});
请您参考如下方法:
您的拦截器需要跟踪它是否有“正在运行”的身份验证请求。它可以通过保留对身份验证请求返回的 promise 的引用来做到这一点。如果有一个请求正在运行并且你得到另一个 401,只需使用缓存的 promise 而不是启动一个新的请求。此外,您应该考虑添加逻辑来解决“/api/auth/refresh”本身返回 401 的情况。
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);
}
};
});