アクセス トークンを使用する API の Angular 2 フロントエンドを作成しています。これでオブザーバブルとngrx/storeを使用しようとしています。
ログインとログアウトは正常に機能し、意図したとおりに機能します。トークンの有効期限が切れたためにリクエストが失敗した場合のコードもいくつか書きました。このコードは通常は問題なく動作しますが、短期間に複数のリクエストがあると問題が発生します。これは、たとえば、ページを更新し、アプリがアプリ全体で必要な 2 つまたは 3 つのストアを埋めようとしたときに発生します。
私の認証サービスには、次のような機能があります。
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()
が私の質問を理解するために必要だとは思いません。
失敗したリクエストが 1 つあり、エラーが 401 の場合、アプリは を呼び出しrefreshLogin()
、新しいアクセス トークンを取得して保存し、新しいアクセス トークンを使用して元のリクエストを再試行します。
失敗したリクエストが複数あり、それらが同時に効果的に発生している場合、問題が発生します。たとえば、2 つの GET リクエストがあるとします。どちらも 401 エラーを返します。どちらもrefreshLogin()
関数を起動します。1 つrefreshLogin()
は成功し、新しいアクセス トークンを保存します。もう 1 つは、更新に使用されているトークンが無効になっているため失敗します。この一連の関数は失敗し、アプリが停止します。
1 つの解決策は、GET 要求を連続してスタックすることですが、そうする必要はないようです。
失敗した GET (またはその他の) 要求で、アプリがアクセス トークンを更新するための呼び出しをトリガーするソリューションが必要だと思います。認証サービスはこれらのリクエストを抑制して、数秒ごとに 1 つだけになるようにします。このリクエストが実行され、新しいアクセス トークンがすべてのリクエストに返され、再試行されます。
これは賢明なアプローチだと思いますか、それとも最初からよく考えられなかったアプローチを修正しようとしているだけですか? これらのパーツをどのように相互作用させることをお勧めしますか?