2016-09-26 58 views
2

我正在爲使用訪問令牌的API製作Angular 2前端。我正在嘗試使用observables和ngrx/store。Angular 2處理需要身份驗證令牌刷新的多個失敗請求

登錄和登出工作正常,並按預期工作。由於令牌過期,請求失敗時,我還寫了一些代碼。這段代碼通常工作正常,但當我在短時間內有多個請求時,我遇到了麻煩。例如,當我刷新頁面時,會發生這種情況,並且應用程序嘗試在應用程序中填充所需的兩到三個商店。


我的身份驗證服務具有這樣的功能:

refreshLogin(): Observable<any> { 
    const username = this.currentUser(); 
    let query = new URLSearchParams(); 
    query.set('refresh_token', this.refreshToken()); 
    query.set('grant_type', 'refresh_token'); 
    return this.getToken(username, query.toString()); 
} 

private getToken(username: string, body: string): Observable<any> { 
    const url = Config.AUTH_TOKEN_PATH; 
    return this.http.post(url, body) 
     .map((res: Response) => res.json()) 
     .do(
      (data) => { 
       const token = { 
        username: username, 
        accessToken: data.access_token, 
        refreshToken: data.refresh_token, 
        tokenType: data.token_type, 
        expiresIn: data.expires_in 
       }; 
       this.store.dispatch(AuthActions.loginSuccess(token)); 
      }, 
      error => { 
       const description = error.json().error_description; 
       this.store.dispatch(AuthActions.loginError(description)); 
       console.error(error); 
      } 
     ) 
    ; 
} 

我的REST功能有這樣的函數:

get(url: string): Observable<any> { 
    return this.http.get(url, new RequestOptions({ headers: this.authHeader() })) 
     .catch(err => { 
      if (err.status === 401) { 
       return this.auth.refreshLogin() 
        .switchMap(() => this.http.get(url, new RequestOptions({ headers: this.authHeader() }))) 
        .catch(err2 => err2); 
      } else { 
       console.log(err); 
      }     
     }) 
     .map((res: Response) => res.json()) 
    ; 
} 

我不覺得功能currentUser()refreshToken(),和authHeader()是理解我的問題所必需的。


如果我有一個請求失敗和錯誤是401,我的應用程序調用refreshLogin(),獲取新的訪問令牌,並將其存儲,和原來的請求與新的訪問令牌再次嘗試。

如果我有更多的失敗請求,並且它們同時發生,我會遇到問題。例如,假設我有兩個GET請求。他們都返回401錯誤。它們都會關閉refreshLogin()功能。一個refreshLogin()成功並存儲新的訪問令牌;另一個失敗,因爲用於刷新的令牌不再有效。這個功能流現在失敗了,導致我的應用程序停滯。

一個解決方案是將我的GET請求串聯起來,但似乎並不是這樣。

我覺得應該有一個解決方案,在失敗的GET(或其他)請求中,應用程序觸發刷新訪問令牌的調用。 auth服務會限制這些請求,所以每隔幾秒鐘就會有一次。它滿足了這個請求,並且新的訪問令牌被返回給所有請求以再次嘗試。

您是否認爲這是一種明智的方法,或者我只是試圖修補一種嚴重思考的方法?你會如何推薦讓這些零件互動?

+1

嘿男人,你有解決這個問題嗎?如果是,你是如何解決它的,我遇到了和你一樣的問題。謝謝! –

+0

Hi @SydneyLoteria。我確實解決了我的問題,謝謝。事實證明,我有正確的想法,但對rxjs沒有足夠的瞭解。我使用'.throttleTime(3000)'在三秒內停止多個刷新請求。不過,我應該補充一點,我也使用了@ ngrx/effects。所以它實際上是另一個被觀察的對象,然後可觀察對象調用'refreshToken'函數。我希望這是有道理的。 – freethebees

+0

throttleTime對我來說是新的:)。所以,你的意思是我將在refreshToken服務上添加.throttleTime(3000),如果它們被多次調用,將導致停止所有活動的refreshToken? –

回答

0

爲了解決這個問題,我創建了一個新的@ ngrx/store動作來刷新我的令牌並使用@ ngrx/effects來使用它。我明白並不是每個人都會想要使用效果,但我發現它在很多場景中都非常有用,而不僅僅是這一個。

所以我的REST得到函數現在看起來是這樣的:

get(url: string): Observable<any> { 
    return this.http.get(url, new RequestOptions({ headers: this.authHeader() })) 
     .catch(err => { 
      if (err.status === 401) { 
       this.store.dispatch({type: 'REFRESH TOKEN'}); 
      } 
      return Observable.of(err); 
     }) 
     .map((res: Response) => res.json()) 
    ; 
} 

這一行動是由我的特效模塊拿起...

@Effect() refreshToken$ = this.actions$ 
    .ofType('REFRESH TOKEN') 
    .throttleTime(3000) 
    .switchMap(() => this.authService.refreshLogin()) 
    .map((response) => { 
     // Store token 
    }) 

同時,功能/動作/無論接收來自REST獲取請求的響應可以確定請求是否成功或者是否由於身份驗證失敗而失敗。如果是後者,它可以再次觸發請求(在等待更新的令牌之後);否則,它可以用另一種方式處理另一種類型的故障。

+0

謝謝曼的樣品。會試試這個。同時學習rxjs和observables,乍一看看起來並不那麼簡單:D –

+0

我已經習慣了rxjs和反應式編程,這讓我花了很長時間。但它很快成爲第二天性。這個介紹可以幫助:https://gist.github.com/staltz/868e7e9bc2a7b8c1f754 – freethebees

+0

這種運作良好,當你只有一個標籤,但如果有兩個選項卡打開了,兩個標籤需要刷新令牌在同一時間?兩個選項卡都獲得一個新的訪問令牌,但其中只有一個會實際工作,另一個選項卡不再能夠發出API請求?這是我遇到的問題:( – Kevin

相關問題