我们正在使用我们的应用程序的刷新 token 逻辑实现 token 授权。基本上,一切正常。但是我们想重试由于 token 过期而失败的请求。一切都在拦截器中完成。这是一些相关代码:
a.service('APIInterceptor', function ($q, $rootScope, $location, $window, $injector) {
var service = this;
var $http;
var refreshTokenInProcess = false;
executeRequest = function (config) {
var accessToken = $window.localStorage.getItem('token');
if (accessToken != 'null') {
config.headers.authorization = "bearer " + accessToken;
}
lastRequest = config;
return config;
};
service.request = function (config) {
return executeRequest(config);
};
var tokenRefreshing = function () {
var deferred = $q.defer();
// Run refresh token service only once in case multiple requests are failing
if (refreshTokenInProcess == false) {
var refreshToken = $window.localStorage.getItem('refresh_token');
var clientId = $window.localStorage.getItem('client_id');
var apiUrl = $window.localStorage.getItem('apiUrl');
var param = "grant_type=refresh_token&refresh_token=" + refreshToken + "&client_id=" + clientId;
$http = $http || $injector.get('$http');
$http.post(apiUrl + 'token', param, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }).
then(function (success) {
$window.localStorage.setItem('token', success.data.access_token);
$window.localStorage.setItem('refresh_token', success.data.refresh_token);
$window.localStorage.setItem('client_id', "web");
$window.localStorage.setItem('expires', success.data[".expires"]);
deferred.resolve(success);
refreshTokenInProcess = false;
}, function (err) {
deferred.reject(err);
});
}
else
deferred.resolve();
refreshTokenInProcess = true;
return deferred.promise;
};
service.responseError = function (response) {
if (response.status === 406 && response.data === "Unauthenticated Token.") {
//retry logic
tokenRefreshing().then(function () {
return $http(executeRequest(response.config)).then(function (data) {
if (data)
response.config.callerController(data.data);
})
});
}
};
当只有一个失败的请求时,一切似乎都正常,但如果我等待足够长的时间(比如一夜之间),我会发现重试进入了一个循环。我正在尝试使用 refreshTokenInProcess 标记 token 刷新,但仍然看到每个失败的请求都获得了 token 刷新。
请给我一些关于这项任务的想法/设计模式。
谢谢
最佳答案
这是我编译的 JavaScript 的 View ,我使用 TypeScript 并提供了该代码。一般来说,我会建议另外两种模式:
- 将“refreshTokenInProcess”存储为除类变量之外的本地存储变量。这将有助于保留一个单一的持久值以指示刷新是否正在进行中。
- 跟踪 retryCount 和 maxRetryCount 以确保不会发生循环。如果超过重试次数,您还可以执行其他一些任务。成功刷新后,retryCount 将重置。
JavaScript
var TestServices = /** @class */ (function () {
function TestServices($window, $injector, $http, $q) {
var _this = this;
this.$window = $window;
this.$injector = $injector;
this.$http = $http;
this.$q = $q;
this.tokenRefreshing = function () {
var deferred = _this.$q.defer();
// Run refresh token service only once in case multiple requests are failing
_this.retryCount++;
var refreshToken = _this.$window.localStorage.getItem('refresh_token');
var clientId = _this.$window.localStorage.getItem('client_id');
var apiUrl = _this.$window.localStorage.getItem('apiUrl');
var param = 'grant_type=refresh_token&refresh_token=' + refreshToken + '&client_id=' + clientId;
_this.$http = _this.$http || _this.$injector.get('$http');
_this.$http.post(apiUrl + 'token', param, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }).
then(function (success) {
_this.$window.localStorage.setItem('token', success.data.access_token);
_this.$window.localStorage.setItem('refresh_token', success.data.refresh_token);
_this.$window.localStorage.setItem('client_id', 'web');
_this.$window.localStorage.setItem('expires', success.data['.expires']);
_this.refreshTokenInProcess = false;
// reset the retry count
_this.retryCount = 0;
deferred.resolve(success);
}, function (err) {
_this.refreshTokenInProcess = false;
deferred.reject(err);
});
return deferred.promise;
};
}
Object.defineProperty(TestServices.prototype, "refreshTokenInProcess", {
get: function () {
if (this.$window) {
this._refreshTokenInProcess = this.$window.localStorage.getItem('refreshTokenInProcess') === 'true';
}
return this._refreshTokenInProcess === true;
},
set: function (value) {
this._refreshTokenInProcess = value === true;
var strValue = value === true ? 'true' : 'false';
if (this.$window) {
this.$window.localStorage.setItem('refreshTokenInProcess', strValue);
}
},
enumerable: true,
configurable: true
});
TestServices.prototype.executeRequest = function (config) {
var accessToken = this.$window.localStorage.getItem('token');
if (accessToken !== 'null') {
config.headers.authorization = 'bearer ' + accessToken;
}
this.lastRequest = config;
return config;
};
TestServices.prototype.request = function (config) {
return this.executeRequest(config);
};
TestServices.prototype.responseError = function (response) {
var _this = this;
if (response.status === 406 && response.data === 'Unauthenticated Token.') {
// retry logic
if (this.refreshTokenInProcess === false && this.retryCount < this.maxRetryCount) {
this.refreshTokenInProcess = true;
this.tokenRefreshing().then(function () {
return _this.$http(_this.executeRequest(response.config)).then(function (data) {
if (data) {
response.config.callerController(data.data);
}
});
});
}
}
return response;
};
return TestServices;
}());
typescript
export class TestServices implements ng.IHttpInterceptor {
private _refreshTokenInProcess: boolean;
get refreshTokenInProcess(): boolean {
if (this.$window) {
this._refreshTokenInProcess = this.$window.localStorage.getItem('refreshTokenInProcess') === 'true';
}
return this._refreshTokenInProcess === true;
}
set refreshTokenInProcess(value: boolean) {
this._refreshTokenInProcess = value === true;
const strValue = value === true ? 'true' : 'false';
if (this.$window) {
this.$window.localStorage.setItem('refreshTokenInProcess', strValue);
}
}
lastRequest: any;
maxRetryCount: 10;
retryCount: 0;
constructor(public $window: ng.IWindowService, public $injector: ng.auto.IInjectorService, public $http: ng.IHttpService, public $q: ng.IQService) {
}
executeRequest(config: ng.IRequestConfig) {
const accessToken = this.$window.localStorage.getItem('token');
if (accessToken !== 'null') {
config.headers.authorization = 'bearer ' + accessToken;
}
this.lastRequest = config;
return config;
}
request(config: ng.IRequestConfig) {
return this.executeRequest(config);
}
tokenRefreshing = () => {
const deferred = this.$q.defer();
// Run refresh token service only once in case multiple requests are failing
this.retryCount++;
const refreshToken = this.$window.localStorage.getItem('refresh_token');
const clientId = this.$window.localStorage.getItem('client_id');
const apiUrl = this.$window.localStorage.getItem('apiUrl');
const param = 'grant_type=refresh_token&refresh_token=' + refreshToken + '&client_id=' + clientId;
this.$http = this.$http || this.$injector.get('$http');
this.$http.post(apiUrl + 'token', param, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }).
then((success: any) => {
this.$window.localStorage.setItem('token', success.data.access_token);
this.$window.localStorage.setItem('refresh_token', success.data.refresh_token);
this.$window.localStorage.setItem('client_id', 'web');
this.$window.localStorage.setItem('expires', success.data['.expires']);
this.refreshTokenInProcess = false;
// reset the retry count
this.retryCount = 0;
deferred.resolve(success);
}, (err: any) => {
this.refreshTokenInProcess = false;
deferred.reject(err);
});
return deferred.promise;
}
responseError(response: any) {
if (response.status === 406 && response.data === 'Unauthenticated Token.') {
// retry logic
if (this.refreshTokenInProcess === false && this.retryCount < this.maxRetryCount) {
this.refreshTokenInProcess = true;
this.tokenRefreshing().then(() => {
return this.$http(this.executeRequest(response.config)).then((data: any) => {
if (data) {
response.config.callerController(data.data);
}
});
});
}
}
return response;
}
}
关于javascript - Angular - 重试失败的请求,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33873465/