【问题标题】:Angular 2 dealing with multiple failed requests requiring an auth token refreshAngular 2 处理需要身份验证令牌刷新的多个失败请求
【发布时间】:2016-09-26 10:41:14
【问题描述】:

我正在为使用访问令牌的 API 制作 Angular 2 前端。我正在尝试使用 observables 和 ngrx/store。

登录和注销工作正常且符合预期。我还编写了一些代码来处理由于令牌过期而导致请求失败的情况。这段代码通常可以正常工作,但是当我在短时间内有多个请求时会遇到麻烦。例如,当我刷新页面并且应用尝试填充整个应用所需的两个或三个商店时,就会发生这种情况。


我的身份验证服务具有以下功能:

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 服务会限制这些请求,因此每隔几秒或某事只能有一个。它满足了这个请求,并将新的访问令牌返回给所有请求以重试。

您认为这是一种明智的方法,还是我只是想修补一种一开始就被错误考虑的方法?您建议如何让这些部分交互?

【问题讨论】:

  • 嘿伙计,你解决这个问题了吗?如果是,您是如何解决的,我遇到了与您相同的问题。谢谢!
  • 嗨@SydneyLoteria。我确实解决了我的问题,谢谢。事实证明我的想法是正确的,但对 rxjs 的了解还不够,无法让它发挥作用。我使用.throttleTime(3000) 在三秒内停止了多个刷新请求。但是,我可能应该补充一点,我也使用了@ngrx/effects。所以它实际上是另一个被限制的可观察对象......然后该可观察对象调用refreshToken 函数。我希望这是有道理的。
  • throttleTime 对我来说是新的 :)。所以你的意思是我会在 refreshToken 服务上添加 .throttleTime(3000) 如果它们被多次调用,这将导致停止所有活动的 refreshToken?
  • 如果你可以给我一些例子的话。我对如何将它用于我的 refresh_token 服务的节流时间有点困惑。非常感谢!
  • 我在我的实现中添加了一个答案。希望对您有所帮助。

标签: authentication angular angular2-observables


【解决方案1】:

为了解决这个问题,我创建了一个新的@ngrx/store 操作来刷新我的令牌并使用@ngrx/effects 使用它。我知道不是每个人都想使用效果,但我发现它在很多场景中都非常有用,而不仅仅是这个。

所以我的 REST get 函数现在看起来像这样:

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 get 请求接收响应的函数/操作/任何东西都可以确定请求是成功还是由于身份验证失败而失败。如果是后者,它可以再次触发请求(在等待更新令牌之后);否则,它可以以不同的方式处理另一种类型的故障。

【讨论】:

  • 感谢 Man 的样品。会试试这个。同时将学习 rxjs 和 observables 乍一看看起来并不那么简单:D
  • 我花了很长时间才习惯 rxjs 和响应式编程。但它很快就变成了第二天性。此介绍可以提供帮助:gist.github.com/staltz/868e7e9bc2a7b8c1f754
  • 这在你只有一个标签的情况下效果很好,但是如果打开了两个标签,并且两个标签都需要同时刷新令牌怎么办?两个选项卡都获得了新的访问令牌,但实际上只有一个可以工作,另一个选项卡不再能够发出 API 请求?这就是我遇到的问题:(
  • 解决这个问题。仍然不是 100% 采用最佳方法。我目前正在使用 localStorage 和 websockets 来管理令牌。对令牌的更新来自 ws 并被存储。这样,如果两个选项卡都保存它,则保存相同的令牌。此外,两个选项卡都可以访问相同的令牌。额外的好处:如果您退出一个选项卡,您将退出另一个选项卡。
猜你喜欢
  • 1970-01-01
  • 2016-02-23
  • 2021-09-06
  • 1970-01-01
  • 2018-05-27
  • 1970-01-01
  • 1970-01-01
  • 2017-11-08
  • 2014-08-17
相关资源
最近更新 更多