web-dev-qa-db-fra.com

Angular 4.3 - Intercepteur HTTP - Actualiser le jeton JWT

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);
  }
}
4
kuceraf

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

7
kuceraf

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);
        });
}
4
Lansana

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
    }
}
3
Alfergon
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);
    }
}
0
Smith Doakes