You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解决BehaviorSubject与异步函数的竞态条件及Angular令牌重复刷新问题

解决Angular拦截器并发请求重复刷新令牌问题

核心思路是共享令牌刷新请求的Observable,让所有并发请求复用同一个刷新流程,避免重复调用刷新接口。

1. 改造AuthService,添加共享刷新逻辑

在AuthService中维护一个用于跟踪当前刷新请求的变量,确保同一时间只有一个刷新请求发出:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { BehaviorSubject, Observable, shareReplay, tap, of, catchError, map } from 'rxjs';

@Injectable({ providedIn: 'root' })
export class AuthService {
  private refreshTokenRequest$: Observable<any> | null = null;
  private tokenSubject = new BehaviorSubject<string | null>(null);

  constructor(private http: HttpClient) {}

  // 获取授权头的方法,处理并发刷新
  headers(): Observable<AuthorizationHeaders> {
    // 检查是否已有正在进行的刷新请求
    if (this.refreshTokenRequest$) {
      // 复用已有的刷新请求
      return this.refreshTokenRequest$.pipe(
        map(() => this.buildAuthorizationHeaders())
      );
    }

    // 检查令牌是否过期,实现你的判断逻辑
    if (this.isTokenExpired()) {
      // 发起新的刷新请求,并共享结果
      this.refreshTokenRequest$ = this.http.post('/api/refresh-token', {}).pipe(
        tap((newToken) => {
          this.tokenSubject.next(newToken);
          // 刷新完成后清空请求引用
          this.refreshTokenRequest$ = null;
        }),
        shareReplay(1), // 共享结果给后续订阅者
        catchError((err) => {
          // 出错时也要清空请求引用,避免后续请求被阻塞
          this.refreshTokenRequest$ = null;
          throw err;
        })
      );

      return this.refreshTokenRequest$.pipe(
        map(() => this.buildAuthorizationHeaders())
      );
    }

    // 令牌未过期,直接返回当前头
    return of(this.buildAuthorizationHeaders());
  }

  private isTokenExpired(): boolean {
    // 实现你的令牌过期判断逻辑
    const token = this.tokenSubject.value;
    // ...自定义判断逻辑
    return false;
  }

  private buildAuthorizationHeaders(): AuthorizationHeaders {
    const token = this.tokenSubject.value;
    return token ? { Authorization: `Bearer ${token}` } : {};
  }
}

type AuthorizationHeaders = { [key: string]: string };

2. 简化拦截器代码

拦截器无需处理异步等待的并发问题,直接订阅AuthService的headers方法即可:

intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
  return this.authService.headers().pipe(
    switchMap((headers) => 
      next.handle(request.clone({ setHeaders: headers }))
    )
  );
}

关键逻辑说明

  • refreshTokenRequest$:存储当前正在进行的刷新请求Observable,确保并发请求复用同一个请求。
  • shareReplay(1):让所有后续订阅者共享同一个刷新请求的结果,彻底避免重复发起请求。
  • 刷新完成/出错时清空refreshTokenRequest$:确保下一次需要刷新时能发起新的请求,不会被旧请求阻塞。

内容的提问来源于stack exchange,提问作者patrick

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 23:42:29