Authentication Angular 2处理需要身份验证令牌刷新的多个失败请求

Authentication Angular 2处理需要身份验证令牌刷新的多个失败请求,authentication,angular,angular2-observables,Authentication,Angular,Angular2 Observables,我正在为一个使用访问令牌的API制作一个Angular 2前端。我正在尝试使用可观测数据和ngrx/存储数据 登录和注销工作正常,符合预期。我还编写了一些代码,用于处理由于令牌过期而导致请求失败的情况。这段代码正常工作,但当我在短时间内有多个请求时,我会遇到麻烦。例如,当我刷新页面,应用程序尝试填充整个应用程序所需的两到三个存储时,就会发生这种情况 我的身份验证服务具有以下功能: refreshLogin(): Observable<any> { const usernam

我正在为一个使用访问令牌的API制作一个Angular 2前端。我正在尝试使用可观测数据和ngrx/存储数据

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


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

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);
            }
        )
    ;
}
refreshLogin():可观察{
const username=this.currentUser();
let query=new URLSearchParams();
query.set('refresh_token',this.refreshToken());
set('grant_type','refresh_token');
返回this.getToken(用户名,query.toString());
}
私有getToken(用户名:string,正文:string):可观察{
const url=Config.AUTH\u令牌\u路径;
返回此.http.post(url,正文)
.map((res:Response)=>res.json())
.做(
(数据)=>{
常量令牌={
用户名:用户名,
accessToken:data.access\u令牌,
refreshToken:data.refresh\u标记,
令牌类型:data.token\u类型,
expiresIn:data.expires\u in
};
this.store.dispatch(AuthActions.loginsAccess(令牌));
},
错误=>{
const description=error.json().error\u description;
this.store.dispatch(AuthActions.loginError(description));
控制台错误(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())
    ;
}
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())
    ;
}
get(url:string):可观察{
返回this.http.get(url,newRequestOptions({headers:this.authHeader()}))
.catch(错误=>{
如果(错误状态===401){
返回此.auth.refreshLogin()
.switchMap(()=>this.http.get(url,新请求选项({headers:this.authHeader()})))
.catch(err2=>err2);
}否则{
控制台日志(err);
}                
})
.map((res:Response)=>res.json())
;
}
我觉得理解我的问题不需要函数
currentUser()
refreshToken()
authHeader()


如果我有一个失败的请求,错误是401,我的应用程序将调用
refreshLogin()
,获取一个新的访问令牌并存储它,然后使用新的访问令牌重试原始请求

如果我有多个失败的请求,并且它们同时有效地发生,我就会遇到问题。例如,假设我有两个GET请求。它们都返回401个错误。它们都启动
refreshLogin()
函数。一个
refreshLogin()
成功并存储新的访问令牌;另一个失败,因为用于刷新的令牌不再有效。这个函数流现在失败了,导致我的应用程序暂停

一种解决方案是将GET请求按系列堆叠,但似乎不必如此

我觉得应该有一个解决方案,在一个失败的GET(或其他)请求中,应用程序触发一个调用以刷新访问令牌。身份验证服务将限制这些请求,因此每隔几秒钟或其他时间只能有一个请求。它完成此请求,并将新的访问令牌返回给所有请求以重试


你认为这是一个明智的方法,还是我只是想修补一个一开始就考虑不周的方法?您建议如何使这些部分交互?

为了解决这个问题,我创建了一个新的@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) {
                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())
    ;
}
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())
    ;
}

同时,从REST get请求接收响应的函数/操作/任何内容都可以确定请求是否成功,或者是否由于身份验证失败而失败。如果是后者,它可以在另一时间发出请求(在等待续订令牌之后);否则,它可以以不同的方式处理另一种类型的故障。

嘿,伙计,你解决了这个问题吗?如果是的,你是怎么解决的,我遇到了和你一样的问题。谢谢你好,西德尼洛特拉。我确实解决了我的问题,谢谢。事实证明,我的想法是正确的,但我对rxjs的了解还不够,无法让它发挥作用。我使用
.throttleTime(3000)
在三秒钟内停止了多个刷新请求。然而,我应该补充一点,我也使用了@ngrx/effects。所以它实际上是另一个被抑制的可观测的物体。。。然后,该可观察对象调用
refreshtToken
函数。我希望这是有意义的。throttleTime对我来说是新的:)。那么你的意思是我将在refreshToken服务上添加.throttleTime(3000),这将导致停止所有活动的refreshToken,如果它们被多次调用的话?如果可以的话,请给我举个例子。在throttleTime上,我对如何将其用于刷新令牌服务有点困惑。非常感谢你!我在实现中添加了一个答案。希望能有帮助。谢谢你的样品。我会试试这个。同时,我将学习rxjs和observables,乍一看,这看起来并不是那么简单:DIt让我花了很长时间才习惯rxjs和反应式编程。但它很快就变成了第二天性。这篇介绍可以帮助您:当您只有一个选项卡时,这很有效,但是如果有两个选项卡打开,并且两个选项卡都需要同时刷新令牌呢?这两个选项卡都会获得一个新的访问令牌,但只有一个选项卡是实际的