Невозможно оформить подписку в перехватчике - PullRequest
3 голосов
/ 28 мая 2020

Я делаю перехватчик JWT для обработки ошибки 401. Моя идея собрать все исходные запросы после 401 в массив, затем обновить sh мой токен, а затем добавить новый заголовок к моему запросу. Вот код:

type CallerRequest = {
    subscriber: Subscriber<any>;
    failedRequest: HttpRequest<any>;
};

@Injectable()
export class RefreshTokenInterceptor implements HttpInterceptor {
  private refreshInProgress: boolean;
  private requests: CallerRequest[] = [];

  constructor(
    public authService: AuthService,
    public customersService: CustomersService,
    private http: HttpClient
    ) {
      this.refreshInProgress = false;
    }

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    if (!req.url.includes('api/')) {
      return next.handle(req);
    }

  const observable = new Observable<HttpEvent<any>>((subscriber) => {
  const originalRequestSubscription = next.handle(req)
    .subscribe((response) => {
      subscriber.next(response);
    },
    (err) => {
      if (err.status === 401) {
        this.handleUnauthorizedError(subscriber, req);
      } else {
        subscriber.error(err);
      }
    },
    () => {
      subscriber.complete();
    });

    return () => {
      originalRequestSubscription.unsubscribe();
    };
  });
  return observable;
  }

  private addToken(request: HttpRequest<any>, token: string) {
    return request.clone({setHeaders: {'Authorization': `Bearer ${token}`}});
  }

  private handleUnauthorizedError(subscriber: Subscriber<any>, request: HttpRequest<any>) {
    this.requests.push({ subscriber, failedRequest: request });
    if (!this.refreshInProgress) {
      this.refreshInProgress = true;
      console.log('go refresh');
      this.authService.refreshToken()
        .pipe(
          finalize(() => {
            this.refreshInProgress = false;
          })
        )
        .subscribe(
          result => {
            console.log(result); // <-- I cannot get here
            this.repeatFailedRequests(this.authService.getAccessToken());

          },
        err => {
          console.log(err); // <-- And cannot get here
        }
      )

    }
  }

  private repeatFailedRequests(authHeader: string) {
    this.requests.forEach((c) => {
      const requestWithNewToken = this.addToken(c.failedRequest, authHeader);
      this.repeatRequest(requestWithNewToken, c.subscriber);
    });
    this.requests = [];
  }

  private repeatRequest(requestWithNewToken: HttpRequest<any>, subscriber: Subscriber<any>) {
    this.http.request(requestWithNewToken).subscribe((res) => {
      subscriber.next(res);
    },
      (err) => {
        if (err.status === 401) {
          this.authService.removeTokens();
        }
        subscriber.error(err);
      },
      () => {
        subscriber.complete();
      });
  }


}

Посмотрите мой метод handleUnauthorizedError. Я не могу получить там результаты.
Кстати, вот мой refreshToken:

  refreshToken() {
    return this.post('api/v0/jwt/refresh/', {refresh: this.getRefreshToken()})
    .pipe(tap((tokens: Tokens) => {
      this.storeTokens(tokens);
    }));

}

Что я делаю не так и как мне решить эту проблему надлежащим образом? путь? Спасибо!

ДОБАВЛЕНО :

Если я попробую перехватчик JWT из этого вопроса Angular 4 Запросы на повторную попытку перехватчика после обновления токена sh (самый популярный ответ) У меня та же проблема - мои результаты от RefreshToken () не обрабатываются.

Ответы [ 2 ]

1 голос
/ 30 мая 2020

Проблема заключалась в правильной обработке запросов JWT refre sh во время refreshInProgress. Вот код, который отлично работает:

type CallerRequest = {
    subscriber: Subscriber<any>;
    failedRequest: HttpRequest<any>;
};

@Injectable()
export class AuthTokenInterceptor implements HttpInterceptor {
  private refreshInProgress: boolean;
  private requests: CallerRequest[] = [];
  private refreshJWTReuest: Subscription;
  private straightRequests: string[];

  constructor(
    public authService: AuthService,
    public customersService: CustomersService,
    private http: HttpClient
    ) {
        this.refreshInProgress = false;
        this.straightRequests = ['currency/'];
      }

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    if (this.straightRequests.some(v => req.url.includes(v))) {
      return next.handle(req);
    }

    if (req.url.includes('api/v0/jwt/create/')) {
      this.refreshInProgress = false;
    }


  const observable = new Observable<HttpEvent<any>>((subscriber) => {
  const originalRequestSubscription = next.handle(this.addToken(req, this.authService.getAccessToken()))
    .subscribe((response) => {
      subscriber.next(response);
    },
    (err) => {
      if (err.status === 401) {
        if (req.url.includes('api/v0/jwt/refresh/')) {
          this.refreshJWTReuest.unsubscribe();
          this.requests = [];
          this.authService.logout();
        }
        this.handleUnauthorizedError(subscriber, req);
      } else {
        // notification!!!
        subscriber.error(err);
      }
    },
    () => {
      subscriber.complete();
    });

    return () => {
      originalRequestSubscription.unsubscribe();
    };
  });
  return observable;
  }

  private addToken(request: HttpRequest<any>, token: string) {
    return request.clone({setHeaders: {'Authorization': `Bearer ${token}`}});
  }

  private handleUnauthorizedError(subscriber: Subscriber<any>, request: HttpRequest<any>) {
    if (!this.refreshInProgress) {
      this.refreshInProgress = true;
      this.refreshJWTReuest = this.authService.refreshToken()
        .subscribe(
          result => {
            this.refreshInProgress = false;
            this.repeatFailedRequests(result.access);
          },
        err => {
          this.authService.logout();
          console.log(err);
        }
      );

    }
    if (!request.url.includes('api/v0/jwt/refresh/')) {
      // avoid refresh requests while refreshInProgress
      // this solves a small bug after logout()
      this.requests.push({ subscriber, failedRequest: request });
    }

  }

  private repeatFailedRequests(authHeader: string) {
    this.requests.forEach((c) => {
      const requestWithNewToken = this.addToken(c.failedRequest, authHeader);
      this.repeatRequest(requestWithNewToken, c.subscriber);
    });
    this.refreshJWTReuest.unsubscribe();
    this.requests = [];
  }

  private repeatRequest(requestWithNewToken: HttpRequest<any>, subscriber: Subscriber<any>) {
    this.http.request(requestWithNewToken).subscribe((res) => {
      subscriber.next(res);
    },
      (err) => {
        if (err.status === 401) {
          this.authService.logout();
        }
        subscriber.error(err);
      },
      () => {
        subscriber.complete();
      });
  }

}
0 голосов
/ 28 мая 2020

Я думаю, проблема в вашем методе перехвата, поскольку вы обертываете originalObservable, он не вызывается должным образом.

Попробуйте что-то вроде этого:

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    if (!req.url.includes('api/')) {
      return next.handle(req);
    }

   return next
      .handle(req)
      .catch((err) => {
          if (err.status === 401) {
            this.handleUnauthorizedError(req);
          } else {
            return next.handle(req);
          }
        })
      );    
  }
...