Angular 6 HTTP Interceptor Refresh token 401 handling

与世无争的帅哥 提交于 2021-02-08 08:52:15

问题


I have built my AuthInterceptor which on a 401 error sends a request to get a new token.

The handle401Error method is called when I encounter a 401 error, But I don't seem to be getting any response in my this.authService.refreshToken().pipe()

AuthInterceptor

import { Injectable, Injector } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpErrorResponse } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError, tap, switchMap } from 'rxjs/operators';
import { AuthService } from '../services/auth.service';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
  isRefreshingToken:boolean = false;
  constructor(public authService: AuthService, private injector: Injector) {}

    addToken(req: HttpRequest<any>, token: string): HttpRequest<any> {
        return req.clone({ setHeaders: {
          Authorization: 'Bearer ' + token,
          'Content-Type': 'application/json'
       }})
    }

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

    request = this.addToken(request, this.authService.getToken())
    return next.handle(request).pipe (
      catchError((error: Error) => {
        if(error instanceof HttpErrorResponse){
          console.log('err', error);
          switch(error.status) {
            case 401:
            this.handle401Error(request, next)
            default:
            return throwError(error);
          }

        } else {
          return throwError(error);
        }
      })
    );
  }

  handle401Error(request: HttpRequest<any>, next: HttpHandler){
    console.log('handle 401');
    if(!this.isRefreshingToken) { //do not send another refresh token request if already in progress
      this.isRefreshingToken = true;

      return this.authService.refreshToken().pipe(
        switchMap((newToken: string) => {
          console.log('newToken', newToken) // I DO NOT GET A VALUE HERE
            if (newToken) { 
              return next.handle(this.addToken(request, newToken));
            }

        }),
        catchError((error) => {
          console.log('error', error) //NOR DO I GET AN ERROR
          return throwError(error)
        }),
        tap(data => console.log(data))
      )
    }
  }
}

AuthService Refresh Token

refreshToken(): Observable<string> {
    console.log('refreshToken');
    let token = 'xxx-xxx-xx';

    return of(token).pipe(delay(1000));
}

回答1:


Besides the missing return in 'case 401', here's how I would handle it.

The basic idea is that you want to retry the entire observable once you receive the new refresh token. Also, the request is patched inside an observable so that on each retry it will get the new token from the auth service.

import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Injectable } from '@angular/core';

import { Observable } from 'rxjs';
import { catchError, retryWhen, switchMap, tap } from 'rxjs/operators';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
  private retryRequest = Symbol('reload');
  private refreshToken = this.authService.refreshToken()
    .pipe(
      tap(newToken => {
        if (newToken) {
          throw this.retryRequest;
        }
      }),
      share(),
    ) as Observable<any>;

  constructor(public authService: AuthService) { }

  private addToken(req: HttpRequest<any>, token: string): HttpRequest<any> {
    return req.clone({
      setHeaders: {
        Authorization: 'Bearer ' + token,
        'Content-Type': 'application/json'
      }
    });
  }

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const request$ = new Observable<HttpRequest<any>>(observer => {
      observer.next(this.addToken(request, this.authService.getToken()));
      observer.complete();
    });

    return request$.pipe(
      switchMap(req => next.handle(req)),
      catchError((error: Error) => {
        if (error instanceof HttpErrorResponse) {
          switch (error.status) {
            case 401:
              return this.refreshToken;
            default:
              throw error;
          }
        } else {
          throw error;
        }
      }),
      retryWhen(err$ =>
        err$.pipe(
          tap(err => {
            if (err === this.retryRequest) { return; }
            throw err;
          })
        )
      )
    );
  }
}


来源:https://stackoverflow.com/questions/52720099/angular-6-http-interceptor-refresh-token-401-handling

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!