问题
I'm using the HttpInterceptor
in Angular 6, and trying to built a refresh token mechanisim:
When httpClient request get's 401
status code (unauthorised) the HttpInterceptor
will create a request that will refresh the token, it will update the headers of the first request and call it again with the new token.
The code is working until the stage that I need to recall the original request again with the new token that got from the refresh token request. This is my code:
export class MyInterceptor implements HttpInterceptor {
constructor(public restService:RestService){}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(request).pipe(
tap(event => {
if (event instanceof HttpResponse) {
console.log('succeed');
}
}, error => {
if(error.status==401){
this.restService.refreshToken().subscribe(response => {
this.restService.updateHeaders(response['token']);
const newRequest = request.clone();
return next.handle(newRequest);
});
}
})
)
}
}
回答1:
You need to do something like below. You also need to make sure the new header will be appended to the request. Not sure where you do it, since it's not in this interceptor. The best part to append it would be in an interceptor. Even this one, in case you're actually doing it in the services.
// if multiple requests fail, do only one refresh request
private readonly refreshToken$ = this.restService
.refreshToken() //the refresh token method could update the token in it's internal state, not sure why the later call to updateHeaders
.pipe(
tap(response => this.restService.updateHeaders(response['token'])),
ignoreElements(),
shareReplay(1)
);
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next
.handle(request)
.pipe(
catchError(error => {
if (error.status === 401) {
return concat(this.refreshToken$, throwError(new RetryRequestError()));
} else {
throw error;
}
}),
retryWhen(error => {
if (error instanceof RetryRequestError) {
// try the request again
return;
}
throw error;
})
);
}
class RetryRequestError extends Error {
constructor() {
super('retry_request');
Object.setPrototypeOf(this, RetryRequestError.prototype);
}
}
回答2:
I read more about it, and used a code that some one wrote, and I changed it a little bit and modified it, and finnaly it worked for me. of course that if someone use this code he should modified it.
import { Observable } from 'rxjs';
import { HttpClient } from '@angular/common/http';
import { catchError, switchMap } from 'rxjs/operators';
import { Injectable } from "@angular/core";
import { HttpInterceptor, HttpRequest, HttpHandler, HttpSentEvent, HttpHeaderResponse, HttpProgressEvent, HttpResponse, HttpUserEvent, HttpErrorResponse } from "@angular/common/http";
import { _throw as observableThrowError } from 'rxjs/observable/throw';
import { Router } from "@angular/router";
import { environment } from '../../../environments/environment'
@Injectable()
export class RequestInterceptorService implements HttpInterceptor {
public endPoints;
constructor(public httpClient: HttpClient, public router: Router) { this.endPoints = environment.endPoints; }
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpSentEvent | HttpHeaderResponse | HttpProgressEvent | HttpResponse<any> | HttpUserEvent<any>> {
return <any>next.handle(req.clone({ headers: req.headers.set('Cache-Control', 'no-cache').set('Pragma', 'no-cache'), withCredentials: true })).pipe(
catchError(error => {
if (req.url.indexOf('refresh_token_login') != -1 && ((<HttpErrorResponse>error).status) == 401) {//the app created request for getting new token/session and got 401,meaning the refresh token/session also expired
this.router.navigate(['/logout']);
}
else if (((<HttpErrorResponse>error).status) == 401 && (<HttpErrorResponse>error).error.error_code == 401001) { // 401001 meaning that the token is invalid
this.router.navigate(['/logout']);
}
else if (((<HttpErrorResponse>error).status) == 401 && (<HttpErrorResponse>error).error.error_code == 401002) { // 401002 meaning that the token has expired
return this.handle401Error(req, next);
} else {
return observableThrowError(error.error);
}
}));
}
handle401Error(req: HttpRequest<any>, next: HttpHandler) {
return this.refreshToken().pipe(
switchMap((res) => {
if (res.status == 200) {
return next.handle(this.getNewRequest(req));
}else{
return this.logoutUser();
}
}),
catchError(error => {
return next.handle(this.getNewRequest(req));
})
)
}
getNewRequest(req: HttpRequest<any>): HttpRequest<any> {
return req.clone({ headers: req.headers.set('Cache-Control', 'no-cache').set('Pragma', 'no-cache'), withCredentials: true });
}
logoutUser() {
this.router.navigate(['/logout']);
return observableThrowError("");
}
refreshToken() {
return this.httpClient.get(this.endPoints.refreshToken, { observe: 'response' }).pipe(
catchError(error => {
return observableThrowError(error);
}));
}
}
来源:https://stackoverflow.com/questions/51786002/angular-6-httpinterceptor-when-getting-401-refresh-the-token-and-create-the-sa