2016-09-26 38 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

相关问题