在Angular 4中为多个api调用刷新令牌

时间:2018-03-14 07:25:17

标签: angular access-token angular-services

我正在尝试在我的网络应用中实现刷新令牌概念。

在页面刷新中,我正在调用4个API,当访问令牌到期时,我正在调用后端以获取基于刷新令牌的新访问令牌。

所以在我的情况下我能够获得新的访问令牌但又无法触发4个API调用,除非我手动进行页面刷新或从服务重新加载页面。但我不想重新加载页面,并希望在不知道最终用户的情况下完成API调用。

给出一些建议或一些想法来做到这一点。

2 个答案:

答案 0 :(得分:1)

您可以使用Angular HttpInterceptor来解决您的问题。请参阅下面的代码段。

@Injectable()
export class KgRequestInterceptorService implements HttpInterceptor {
  authenticationService: MyAuthenticationService;
  snackbarService: KgSnackbarService

  constructor(private injector: Injector) { }

  addBearerAndHeaders(req: HttpRequest<any>, token: string, overwrite?: boolean): HttpRequest<any> {
    reqHeaders = reqHeaders.set("Authorization", 'Bearer ' + token);
    return req.clone({ headers: reqHeaders });
  }

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpSentEvent | HttpHeaderResponse | HttpProgressEvent | HttpResponse<any> | HttpUserEvent<any>> {
    this.authenticationService = this.authenticationService ? this.authenticationService : this.injector.get<MyAuthenticationService>(MyAuthenticationService);
    return next.handle(this.addBearerAndHeaders(req, this.authenticationService.accessToken)).pipe(
      catchError((error, cought) => {
        if (error instanceof HttpErrorResponse) {
          switch ((<HttpErrorResponse>error).status) {
            case 400:
              return this.handle400Error(error);
            case 401:
              return this.handle401Error(req, next);
            case 403:
              return this.handle403Error(error);
            default:
              return _throw(error);
          }
        } else {
          return _throw(error);
        }
      })

    )
  }

  handle401Error(req: HttpRequest<any>, next: HttpHandler) {
    if (!this.isRefreshingToken) {
      this.tokenSubject.next(null);
      this.isRefreshingToken = true;
      console.log("isRefreshingToken", this.isRefreshingToken);


      // Reset here so that the following requests wait until the token
      // comes back from the refreshToken call.
      this.authenticationService = this.authenticationService ? this.authenticationService : this.injector.get<KgAuthenticationService>(KgAuthenticationService);
      this._location = this._location ? this._location : this.injector.get<Location>(Location);

      return this.authenticationService.renewToken().pipe(
        switchMap((newToken: string) => {
          if (newToken) {
            console.log("newToken Recieved:", newToken);

            this.tokenSubject.next(newToken);
            this.authenticationService.storeRenewedToken(newToken);
            return next.handle(this.addBearerAndHeaders(req, newToken, true));
          }

          // If we don't get a new token, we are in trouble so logout.
          //return this.logout();
        }),
        catchError(error => {
          // If there is an exception calling 'refreshToken', bad news so logout.
          //return this.logout();
        }),
        finalize(() => {
          this.isRefreshingToken = false;
          console.log("isRefreshingToken", this.isRefreshingToken);
        })
      );
    } else {
      return this.tokenSubject.pipe(
        filter(token => token != null),
        take(1),
        switchMap(token => {
          console.log("newtoken:", token.substr(token.length - 20, token.length - 1))
          return next.handle(this.addBearerAndHeaders(req, token, true));
        })
      )
    }
  }

  handle400Error(error) {
    if (error && error.status === 400 && error.error && error.error.error === 'invalid_grant') {
      // If we get a 400 and the error message is 'invalid_grant', the token is no longer valid so logout.
      return this.logoutUser();
    }

    return _throw(error);
  }

  handle403Error(error) {
    if (error.status === 403) { }
    return _throw(error);
  }
}

有关此问题的好文章位于https://www.intertech.com/Blog/angular-4-tutorial-handling-refresh-token-with-new-httpinterceptor/

答案 1 :(得分:0)

在这种情况下,最好使用switchMap 如果你有一个Observable并且你需要从另一个请求中获取一些东西并返回一个不同的Observable,你可以使用SwitchMap: https://blog.angular-university.io/rxjs-switchmap-operator/

ngOnInit() {
    this._moviesDataService.getShowtimes()
        .switchMap(res => { 
            const id = Object.keys(res[0].showtimes)[0]; // assuming you have one element in your array and you want the first id from showtimes
            return this.getMovies(id); // assuming, you have a separate method that returns the movies
        })
        .subscribe(res => this.results = res)
}