无法在拦截器中进行订阅

Can not make subscription in interceptor

我正在制作 JWT 拦截器来处理 401 错误。我的想法是将 401 之后的所有原始请求收集到数组中,然后刷新我的令牌,然后将新的 header 添加到我的请求中。这是代码:

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

}

我做错了什么,我该如何正确解决这个问题?谢谢!

添加:

如果我从这个问题 Angular 4 Interceptor retry requests after token refresh(最流行的答案)中尝试 JWT 拦截器,我会遇到同样的问题 - 我的 RefreshToken() 结果未得到处理。

我认为问题出在你的拦截方法上,因为你包装了原始的 Observable,它没有被正确调用。

尝试这样的事情:

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

问题在于在 refreshInProgress 时正确处理 JWT 刷新请求。这是完美运行的代码:

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();
      });
  }

}