Angular HTTP 拦截器等待 http 请求直到获得刷新令牌
Angular HTTP Interceptor wait http requests until get a refresh token
我构建了我的 AuthInterceptor,它在出现 401 错误时发送请求以获取新令牌。
当我遇到 401 错误时会调用 handle401Error 方法,但我正在尝试等待其他 HTTP 请求,直到我获得新令牌。但是它并没有等到获得新的刷新令牌,尽管它正在对 HTTP 调用并获取新的访问令牌。请找到我附上的屏幕截图。
Interceptor.ts
isRefreshingToken = false;
tokenSubject: BehaviorSubject<string | null> = new BehaviorSubject<string | null>(null);
intercept(
request: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
const timeOut = appSettings.ajaxTimeout;
const retryCount = appSettings.retryCount;
const retryDelay = appSettings.retryDelayMS;
return next.handle(request).pipe(
timeout(timeOut),
catchError((error) => {
if (error instanceof HttpErrorResponse) {
const httpErrorCode: number = error['status'];
switch (httpErrorCode) {
case StatusCodes.BAD_REQUEST:
return throwError(error);
//return this.handle400Error(error);
case StatusCodes.UNAUTHORIZED:
return this.handle401Error(request, next);
default:
this._toastr.error(
'Sorry! something went wrong.',
'Error!'
);
return throwError(error);
}
} else {
return throwError(error);
}
}),
retryWhen((errors) => {
return errors.pipe(
concatMap((error, count) => {
if (count < retryCount) {
return of(error);
}
return throwError(error);
}),
delay(retryDelay)
);
})
);
}
handle401Error(
request: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
if (!this.isRefreshingToken) {
this.isRefreshingToken = true;
console.log('401');
// Reset here so that the following requests wait until the token
// comes back from the refreshToken call.
this.tokenSubject.next(null);
return this._spotify.getRefreshToken().pipe(
switchMap((authData: ISpotifyTokens) => {
if (authData) {
console.log('new token success');
this.updateTokenInCookie(authData);
this.tokenSubject.next(authData.access_token);
return next.handle(request);
}
return this.logoutUser();
}),
catchError((error) => {
// If there is an exception calling 'refreshToken', bad news so logout.
return this.logoutUser();
}),
finalize(() => {
this.isRefreshingToken = false;
})
);
} else {
return next.handle(request);
}
}
updateTokenInCookie(authData: ISpotifyTokens) {
this._spotify.updateTokensInStorage(authData);
this._spotify.startRefreshTokenTimer(authData.expires_in);
}
logoutUser() {
this._spotify.clearTokensFromStorage();
this._router.navigate(['/welcome']);
return throwError('');
}
我已经使用下面的代码解决了这个问题。如果谁有更好的解决方案,请提出来。
private isRefreshingToken = false;
private timeOut = appSettings.ajaxTimeout;
private tokenSubject: BehaviorSubject<any> = new BehaviorSubject<any>(null);
intercept(
request: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
return next.handle(request).pipe(
timeout(this.timeOut),
catchError((error) => {
if (error instanceof HttpErrorResponse) {
const httpErrorCode: number = error['status'];
switch (httpErrorCode) {
case StatusCodes.BAD_REQUEST:
return throwError(error);
case StatusCodes.UNAUTHORIZED:
return this.handle401Error(request, next);
default:
this._toastr.error(
'Sorry! something went wrong.',
'Error!'
);
return throwError(error);
}
} else {
return throwError(error);
}
})
);
}
private handle401Error(
request: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
if (!this.isRefreshingToken) {
this.isRefreshingToken = true;
// Reset here so that the following requests wait until the token
// comes back from the refreshToken call.
this.tokenSubject.next(null);
return this._spotify.getRefreshToken().pipe(
switchMap((response: ISpotifyTokens) => {
console.log('updating on 401');
// Updating new token in cookie
this._spotify.updateTokensInStorage(response, false);
this.tokenSubject.next(response.access_token);
return next.handle(
this.addTokenInHeader(request, response.access_token)
);
}),
catchError((error) => {
// If there is an exception calling 'refreshToken', bad news so logout.
this.logoutUser();
return throwError('');
}),
finalize(() => {
this.isRefreshingToken = false;
})
);
} else {
return this.tokenSubject.pipe(
filter((token) => token != null),
take(1),
switchMap((token) => {
return next.handle(this.addTokenInHeader(request, token));
})
);
}
}
addTokenInHeader(
request: HttpRequest<any>,
token: string
): HttpRequest<any> {
return request.clone({
setHeaders: { Authorization: 'Bearer ' + token }
});
}
logoutUser(): void {
this._spotify.clearTokensFromStorage();
this._router.navigate(['/welcome']);
}
我构建了我的 AuthInterceptor,它在出现 401 错误时发送请求以获取新令牌。
当我遇到 401 错误时会调用 handle401Error 方法,但我正在尝试等待其他 HTTP 请求,直到我获得新令牌。但是它并没有等到获得新的刷新令牌,尽管它正在对 HTTP 调用并获取新的访问令牌。请找到我附上的屏幕截图。
Interceptor.ts
isRefreshingToken = false;
tokenSubject: BehaviorSubject<string | null> = new BehaviorSubject<string | null>(null);
intercept(
request: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
const timeOut = appSettings.ajaxTimeout;
const retryCount = appSettings.retryCount;
const retryDelay = appSettings.retryDelayMS;
return next.handle(request).pipe(
timeout(timeOut),
catchError((error) => {
if (error instanceof HttpErrorResponse) {
const httpErrorCode: number = error['status'];
switch (httpErrorCode) {
case StatusCodes.BAD_REQUEST:
return throwError(error);
//return this.handle400Error(error);
case StatusCodes.UNAUTHORIZED:
return this.handle401Error(request, next);
default:
this._toastr.error(
'Sorry! something went wrong.',
'Error!'
);
return throwError(error);
}
} else {
return throwError(error);
}
}),
retryWhen((errors) => {
return errors.pipe(
concatMap((error, count) => {
if (count < retryCount) {
return of(error);
}
return throwError(error);
}),
delay(retryDelay)
);
})
);
}
handle401Error(
request: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
if (!this.isRefreshingToken) {
this.isRefreshingToken = true;
console.log('401');
// Reset here so that the following requests wait until the token
// comes back from the refreshToken call.
this.tokenSubject.next(null);
return this._spotify.getRefreshToken().pipe(
switchMap((authData: ISpotifyTokens) => {
if (authData) {
console.log('new token success');
this.updateTokenInCookie(authData);
this.tokenSubject.next(authData.access_token);
return next.handle(request);
}
return this.logoutUser();
}),
catchError((error) => {
// If there is an exception calling 'refreshToken', bad news so logout.
return this.logoutUser();
}),
finalize(() => {
this.isRefreshingToken = false;
})
);
} else {
return next.handle(request);
}
}
updateTokenInCookie(authData: ISpotifyTokens) {
this._spotify.updateTokensInStorage(authData);
this._spotify.startRefreshTokenTimer(authData.expires_in);
}
logoutUser() {
this._spotify.clearTokensFromStorage();
this._router.navigate(['/welcome']);
return throwError('');
}
我已经使用下面的代码解决了这个问题。如果谁有更好的解决方案,请提出来。
private isRefreshingToken = false;
private timeOut = appSettings.ajaxTimeout;
private tokenSubject: BehaviorSubject<any> = new BehaviorSubject<any>(null);
intercept(
request: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
return next.handle(request).pipe(
timeout(this.timeOut),
catchError((error) => {
if (error instanceof HttpErrorResponse) {
const httpErrorCode: number = error['status'];
switch (httpErrorCode) {
case StatusCodes.BAD_REQUEST:
return throwError(error);
case StatusCodes.UNAUTHORIZED:
return this.handle401Error(request, next);
default:
this._toastr.error(
'Sorry! something went wrong.',
'Error!'
);
return throwError(error);
}
} else {
return throwError(error);
}
})
);
}
private handle401Error(
request: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
if (!this.isRefreshingToken) {
this.isRefreshingToken = true;
// Reset here so that the following requests wait until the token
// comes back from the refreshToken call.
this.tokenSubject.next(null);
return this._spotify.getRefreshToken().pipe(
switchMap((response: ISpotifyTokens) => {
console.log('updating on 401');
// Updating new token in cookie
this._spotify.updateTokensInStorage(response, false);
this.tokenSubject.next(response.access_token);
return next.handle(
this.addTokenInHeader(request, response.access_token)
);
}),
catchError((error) => {
// If there is an exception calling 'refreshToken', bad news so logout.
this.logoutUser();
return throwError('');
}),
finalize(() => {
this.isRefreshingToken = false;
})
);
} else {
return this.tokenSubject.pipe(
filter((token) => token != null),
take(1),
switchMap((token) => {
return next.handle(this.addTokenInHeader(request, token));
})
);
}
}
addTokenInHeader(
request: HttpRequest<any>,
token: string
): HttpRequest<any> {
return request.clone({
setHeaders: { Authorization: 'Bearer ' + token }
});
}
logoutUser(): void {
this._spotify.clearTokensFromStorage();
this._router.navigate(['/welcome']);
}