Angular5+ngx-restangular:多过期令牌重复刷新令牌问题
解决Angular 5 + ngx-restangular并发请求时的令牌刷新重复问题
这个问题我之前做项目时也踩过坑,本质就是并发请求触发了重复的令牌刷新操作——当两个请求同时返回401未授权时,各自都去发起刷新令牌的请求,第一个请求拿到新令牌后重试成功,但第二个请求的刷新请求要么因为后端拒绝重复刷新(比如刷新令牌只能用一次),要么因为本地状态混乱直接失败。
核心思路很明确:把刷新令牌的请求变成一个共享的可观察对象,确保同一时间只有一个刷新请求在执行,其他需要刷新令牌的请求都等待这个请求完成后,用新令牌重试。
下面是具体的实现步骤:
1. 在Restangular配置服务中维护刷新请求缓存
首先在你的Restangular配置服务里,加一个变量来保存正在进行的刷新请求,避免重复发起:
// 用来缓存正在进行的刷新令牌请求 private refreshTokenRequest: Observable<any> | null = null;
2. 重写错误拦截逻辑,加入并发控制
在setErrorInterceptor里,不要直接触发刷新,先检查是否已有刷新请求在进行:
RestangularProvider.setErrorInterceptor((response, subject, responseHandler) => { // 只处理401未授权的场景 if (response.status === 401) { // 检查是否已经有刷新请求在执行 if (!this.refreshTokenRequest) { // 如果没有,发起刷新令牌请求并缓存这个Observable this.refreshTokenRequest = this.authService.refreshToken() .pipe( tap((newTokens) => { // 更新本地存储的新访问令牌 this.authService.updateAccessToken(newTokens.accessToken); // 刷新完成后清空缓存,确保下一次过期能正常发起新请求 this.refreshTokenRequest = null; }), catchError((error) => { // 刷新失败时清空缓存,并处理登出逻辑 this.refreshTokenRequest = null; this.authService.logout(); return throwError(() => error); }) ); } // 所有需要刷新的请求都订阅这个共享的刷新Observable this.refreshTokenRequest.subscribe({ next: () => { // 用新令牌克隆并重发原请求 const newRequest = response.request.clone({ setHeaders: { Authorization: `Bearer ${this.authService.getAccessToken()}` } }); // 将重发请求的结果传给原请求的subject this.http.request(newRequest).subscribe(subject); }, error: (err) => { subject.error(err); } }); // 返回false,告诉Restangular不要执行默认的错误处理逻辑 return false; } // 其他错误交给默认处理流程 return true; });
3. 配合后端确保刷新接口的幂等性(可选但推荐)
和后端开发同学确认:刷新令牌的接口是否支持并发调用?如果后端限制一个刷新令牌只能用一次,那上面的缓存逻辑就更关键了——它能从根源上避免重复发送刷新请求。
关键原理说明
- 第一个触发401的请求会发起刷新请求,并把这个Observable缓存起来
- 后续的401请求不会再发新的刷新请求,而是直接订阅已有的那个Observable
- 刷新完成后,所有等待的请求都会用新令牌重试,同时清空缓存,不影响下一次令牌过期的处理
这样不管同时有多少个请求触发401,都只会有一个刷新请求被发送,完美解决你遇到的并发场景问题。
内容的提问来源于stack exchange,提问作者Vahid
相关产品推荐
相关产品推荐

