簡體   English   中英

使用HttpInterceptor刷新並行HTTP請求的令牌

[英]Refreshing token for parallel HTTP requests using HttpInterceptor

我正在開發Ionic應用程序,並嘗試在用戶收到HTTP請求的401響應時兌現刷新令牌。 我發現了一些在線示例,並且可以使該示例( https://www.intertech.com/Blog/angular-4-tutorial-handling-refresh-token-with-new-httpinterceptor/ )與一次出現多個請求的情況除外。

我遇到的問題是一系列調用中的第一個調用會調用刷新令牌並成功重試,而其他調用則永遠不會重試。 如果我在進行刷新的請求中使用.filter並刪除.sub主題返回,則將重試調用,但不會使用新令牌。 關於可觀察性和主題,我還很陌生,所以我不太確定問題可能出在哪里。

要求

  this.myService.getData().subscribe(response => {this.data = response.data;}); 
  this.myService.getMoreData().subscribe(response => {this.moreData = response.data;}); 
  this.myService.getEvenMoreData().subscribe(response => {this.evenMoreData = response.data;}); 

攔截器

@Injectable()
export class HttpInterceptor implements HttpInterceptor {

  isRefreshingToken: boolean = false;
  tokenSubject = new BehaviorSubject<string>(null);   

  tokenService: tokenService;

  constructor(private authService: AuthService, private injector: Injector) { }

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

    return this.authService.getUser().flatMap(user => {
      request = this.addToken(request, next, user.accessToken);

      return next
        .handle(request)
        .catch(error => {
          if (error instanceof HttpErrorResponse) {
            switch ((<HttpErrorResponse>error).status) {
              case 401:
                return this.handle401(request, next, user);
            }
          } else {
          return Observable.throw(error);
        };
      })
    });


  }

  addToken(request: HttpRequest<any>, next: HttpHandler, accessToken: string): HttpRequest<any> {
    return request.clone({ setHeaders: { Authorization: 'Bearer ' + accessToken }})
  }

  handle401(request: HttpRequest<any>, next: HttpHandler, user: any) {

    if (!this.isRefreshingToken) {
      this.isRefreshingToken = true;
      this.tokenSubject.next(null);
      this.tokenService = this.injector.get(tokenService);
      return this.tokenService.refresh(user.refreshToken)
        .switchMap(refreshResponse => {
          if (refreshResponse) {
            this.authService.setUser(refreshResponse.id_token, refreshResponse.access_token, refreshResponse.refresh_token);
            this.tokenSubject.next(refreshResponse.accessToken);
            return next.handle(this.addToken(request, next, refreshResponse.access_token));
          }
          else {
             //no token came back. probably should just log user out.
          }
        })
        .finally(() => {
          this.isRefreshingToken = false;
      });      
    }
    else {
      return this.tokenSubject
        .filter(token => token != null)
        .take(1)
        .switchMap(token => {
          return next.handle(this.addToken(request, next, token));
        });
    }

  }



}

我過去也遇到過類似的問題。 由於某種未知的原因(至少對我而言),當我攔截401時,我進行了刷新並重試,但重試操作被取消了。

盡管如此,我意識到我可以在客戶端讀取JWT到期,因此我通過節省令牌到期時間來欺騙系統。 然后,我使路由事件(例如onViewWillEnter )檢查到期時間,如果令牌已過期,則刷新它。

該機制對用戶是完全透明的,可確保如果用戶在不執行HTTP請求的情況下停留太長時間,則auth令牌或refresh令牌將過期,並且最重要的是,由於您永遠不會獲得401響應(因此在您的情況下,它會轉換為三個請求)。

一種簡單的方法是使用警衛:

canActivate(route: ActivatedRouteSnapshot,
  state: RouterStateSnapshot) {
    if (this.refreshTokenService.isExpired) {
        this.tokenEvent_.next();
        return false;
    } else {
        this.refreshTokenService.refresh();
    }

其中refreshTokenService是具有令牌的實用程序服務,以及一種用於通過HTTP執行刷新的方法。 tokenEvent是一個rxjs/Subject :在防護構造函數中進行了訂閱,每次出現新事件時,它將重定向到登錄頁面。

在每條路由上都添加此保護措施可確保令牌始終未過期。

實際上,通過將主題移至我的auth服務並在setUser方法中進行了下一個操作,最終解決了這一問題。 然后在401方法中的else語句中,我從auth服務上的新方法返回了主題,並對其進行了修復。 我仍然需要take(1),但是由於我最終沒有使用BehaviorSubject,因此能夠擺脫過濾器。

在我看來,您沒有正確的令牌:

  • 你有過:

    this.tokenSubject.next(refreshResponse.accessToken);

  • 應該:

    this.tokenSubject.next(refreshResponse.access_token);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM