AngularJS - HTTP拦截 - 重发令牌刷新毕竟要求 [英] AngularJS - http interceptor - resend all request after token refresh
问题描述
我有一个角度应用程序,它有时确实每个州的多个$ http.get请求。该应用程序usees智威汤逊与刷新令牌用户认证。 API服务器发送 401
对每一个失败,因为身份验证错误的请求。
我做了一个 HTTP拦截
请求一个新的令牌与401错误的刷新令牌并重新发送原始请求后。
问题是,如果一国如2 $ http.get请求,都得到401的响应,那么我续订访问令牌的两倍。很显然,我只想刷新令牌一次,但我还是想重新发送BOTH失败的请求。
这是可以实现的,如果是这样如何?
app.factory('AuthInterceptor',函数($ Q $喷油器,RESOURCE_URL,API_BASE,authService){
返回{
要求:功能(配置){
config.headers = config.headers || {};
如果(authService.getAccessToken()){
如果(config.url.substring(0,RESOURCE_URL.length)!== RESOURCE_URL){
config.headers.Authorization ='承载'+ authService.getAccessToken();
}
}
返回配置;
},
responseError:功能(响应){
开关(response.status){
案例401:
变种推迟= $ q.defer();
。$ injector.get($ HTTP)后(API_BASE +'/ API / auth /中刷新',{refreshtoken:authService.getRefreshToken()}),然后(函数(R){
如果(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)。然后(功能(RESP){
deferred.resolve(RESP);
},功能(RESP){
deferred.reject();
});
}其他{
deferred.reject();
}
},函数(响应){
deferred.reject();
authService.clear();
。$ injector.get($状态)去('guest.login');
返回;
});
返回deferred.promise;
打破;
默认:
authService.clear();
。$ injector.get($状态)去('guest.login');
打破;
}
返回响应|| $ q.when(响应);
}
};
});
您拦截器需要跟踪它是否具有飞行中的认证请求。它可以通过保持由认证请求返回的承诺一个参照。如果在飞行的请求,你会得到另一个401,只使用缓存的承诺,而不是发起一个新的请求。
app.factory('AuthInterceptor',函数($ Q $喷油器,RESOURCE_URL,API_BASE,authService){
VAR inFlightAuthRequest = NULL;
返回{
要求:功能(配置){
config.headers = config.headers || {};
如果(authService.getAccessToken()){
如果(config.url.substring(0,RESOURCE_URL.length)!== RESOURCE_URL){
config.headers.Authorization ='承载'+ authService.getAccessToken();
}
}
返回配置;
},
responseError:功能(响应){
开关(response.status){
案例401:
变种推迟= $ q.defer();
如果(!inFlightAuthRequest){
。inflightAuthRequest = $ injector.get($ HTTP)后(API_BASE +'/ API / auth /中刷新',{refreshtoken:authService.getRefreshToken()});
}
inflightAuthRequest.then(函数(R){
inflightAuthRequest = NULL;
如果(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)。然后(功能(RESP){
deferred.resolve(RESP);
},功能(RESP){
deferred.reject();
});
}其他{
deferred.reject();
}
},函数(响应){
inflightAuthRequest = NULL;
deferred.reject();
authService.clear();
。$ injector.get($状态)去('guest.login');
返回;
});
返回deferred.promise;
打破;
默认:
authService.clear();
。$ injector.get($状态)去('guest.login');
打破;
}
返回响应|| $ q.when(响应);
}
};
});
I have an angular app which sometimes does multiple $http.get requests per state. The app usees JWT for user auth with refresh tokens. The API server sends 401
on every request that failed because of auth error.
I've made an http interceptor
that requests a new token with the refresh token on 401 errors and after that resends the original request.
The problem is, if a state makes for example 2 $http.get requests and both get 401 response then I renew the access token twice. Obviously I only want to refresh the token once, BUT I still want to resend BOTH failed requests.
Is this achievable and if so how?
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);
}
};
});
Your interceptor needs to keep track of whether or not it has an authentication request "in flight". It can do this by keeping a reference to the promise returned by the authentication request. If there is a request in flight and you get another 401, just use that cached promise instead of initiating a new request.
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);
}
};
});
这篇关于AngularJS - HTTP拦截 - 重发令牌刷新毕竟要求的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!