J'ai besoin de réagir (en classe d'intercepteur) sur le jeton JWT 403 HTTP interdit (pour obtenir/actualiser) et réessayer la demande avec un nouveau jeton.
Dans le code ci-dessous, lorsque le serveur renvoie une réponse d'erreur, il appelle callback success (pas dans le rappel d'erreur comme prévu) et l'événement est typeof object (ce qui est inutile en réaction à une réponse d'erreur). L'objet événement ressemble à ceci: {Type: 0}.
Question:
-Comment gérer correctement httpErrorResponse (403 Forbidden) dans HttpInterceptor lorsque j'ai besoin d'actualiser accessToken et de réessayer la demande http?
import {
HttpInterceptor,
HttpRequest,
HttpResponse,
HttpHandler,
HttpEvent
} from '@angular/common/http';
import 'rxjs/add/operator/map';
@Injectable()
class JWTInterceptor implements HttpInterceptor {
constructor(private tokenService: TokenService) {}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
let myHeaders = req.headers;
if (this.tokenService.accessToken) {
myHeaders = myHeaders.append('Authorization',`${this.tokenService.accessToken.token_type} ${this.tokenService.accessToken.access_token}`)
}
const authReq = req.clone({headers: myHeaders});
return next.handle(authReq).map((event: HttpEvent<any>) => {
if (event instanceof HttpResponse) {
// success callback
}
}, (err: any) => {
if (err instanceof HttpErrorResponse {
if (err.status === 403) {
// error callback
this.tokenService.obtainAccessToken()
}
}
})
.retry(1);
}
}
Ma solution finale à ce problème:
@Injectable()
export class WebApiInterceptor implements HttpInterceptor {
constructor(private tokenService: TokenService) {
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
console.log('*An intercepted httpRequest*', req, this.tokenService.accessToken);
const authReq = this.authenticateRequest(req);
console.log('*Updated httpRequest*', authReq);
return next.handle(authReq)
.map((event: HttpEvent<any>) => {
if (event instanceof HttpResponse) {
console.log('*An intercepted httpResponse*', event);
return event;
}
})
.catch((error: any) => {
if (error instanceof HttpErrorResponse) {
if (error.status === 403 && error.url !== environment.authEndpoint) {
return this.tokenService
.obtainAccessToken()
.flatMap((token) => {
const authReqRepeat = this.authenticateRequest(req);
console.log('*Repeating httpRequest*', authReqRepeat);
return next.handle(authReqRepeat);
});
}
} else {
return Observable.throw(error);
}
})
}
}
Une fonction
authenticateRequest(req)
ajoute simplement l'en-tête d'autorisation à la copie de la demande d'origine
Une fonction
obtainAccessToken()
obtenir un nouveau serveur d'autorisation de formulaire de jeton et le stocker
Vous devez ajouter l'opérateur catch
de RxJS. C’est là qu’une erreur se produira et vous pourrez la gérer en conséquence.
Lorsque vous obtenez une erreur de statut 0, cela signifie très probablement que le serveur distant est en panne et que la connexion n'a pas pu être établie.
Jetez un oeil à mon exemple de logique:
this.http.request(url, options)
.map((res: Response) => res.json())
.catch((error: any) => {
const err = error.json();
// Refresh JWT
if (err.status === 403) {
// Add your token refresh logic here.
}
return Observable.throw(err);
});
Pour que votre logique d'actualisation passe par l'intercepteur, vous devez renvoyer l'invocation et la fonction doit également renvoyer une Observable
. Par exemple, en modifiant la logique d'origine ci-dessus:
this.http.request(url, options)
.map((res: Response) => res.json())
.catch((error: any) => {
const err = error.json();
// Refresh JWT
if (err.status === 403) {
// refreshToken makes another HTTP call and returns an Observable.
return this.refreshToken(...);
}
return Observable.throw(err);
});
Si vous souhaitez pouvoir réessayer la demande d'origine, vous pouvez alors transmettre les données de la demande d'origine afin que l'actualisation réussie du jeton vous permette de relancer l'appel. Par exemple, voici à quoi pourrait ressembler votre fonction refreshToken
:
refreshToken(url: stirng, options: RequestOptionsArgs, body: any, tokenData: any): Observable<any>
return this.post(`${this.url}/token/refresh`, tokenData)
.flatMap((res: any) => {
// This is where I retry the original request
return this.request(url, options, body);
});
}
Je voulais juste partager ce qui a fonctionné pour moi:
@Injectable()
export class AutoReLoginInterceptor implements HttpInterceptor {
constructor() {
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// as we want to intercept the possible errors, instead of directly returning the request execution, we return an Observable to control EVERYTHING
return new Observable<HttpEvent<any>>(subscriber => {
// first try for the request
next.handle(req)
.subscribe((event: HttpEvent<any>) => {
if (event instanceof HttpResponse) {
// the request went well and we have valid response
// give response to user and complete the subscription
subscriber.next(event);
subscriber.complete();
}
},
error => {
if (error instanceof HttpErrorResponse && error.status === 401) {
console.log('401 error, trying to re-login');
// try to re-log the user
this.reLogin().subscribe(authToken => {
// re-login successful -> create new headers with the new auth token
let newRequest = req.clone({
headers: req.headers.set('Authorization', authToken)
});
// retry the request with the new token
next.handle(newRequest)
.subscribe(newEvent => {
if (newEvent instanceof HttpResponse) {
// the second try went well and we have valid response
// give response to user and complete the subscription
subscriber.next(newEvent);
subscriber.complete();
}
}, error => {
// second try went wrong -> throw error to subscriber
subscriber.error(error);
});
});
} else {
// the error was not related to auth token -> throw error to subscriber
subscriber.error(error);
}
});
});
}
/**
* Try to re-login the user.
*/
private reLogin(): Observable<string> {
// obtain new authorization token and return it
}
}
import { Injectable } from '@angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable()
export class JwtInterceptor implements HttpInterceptor {
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// add authorization header with jwt token if available
const currentUser = JSON.parse(sessionStorage.getItem('currentUser'));
console.log('Interceter called');
console.log(currentUser);
// const re = 'https://newsapi.org';
const re = '/user';
if (request.url.search(re) === -1) {
if (currentUser && currentUser.token) {
console.log('Token is being added....!!!!!');
// console.log(currentUser.token);
request = request.clone({
setHeaders: {
Authorisation: `Token ${currentUser.token}`,
}
});
}
console.log('Request Sent :');
console.log(request);
}
return next.handle(request);
}
}