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

Angular拦截器等待Refresh Token刷新完成再转发请求的实现方案

Angular拦截器等待JWT刷新完成再处理请求的解决方案

问题现状

当前拦截器检测到access token失效时,直接调用refreshToken方法,但该方法属于异步操作,拦截器未等待token刷新完成就携带旧token发送请求,导致首次请求报错,后续请求因token已刷新完成才正常工作。

修改方案

核心思路是让refreshToken返回Observable,拦截器通过RxJS操作符等待刷新流程完成后再处理请求,解决异步竞态问题。

1. 重构refreshToken方法

不在方法内部直接执行subscribe,而是返回HTTP请求的Observable,将订阅逻辑交给拦截器控制,以此保障异步流程的顺序:

refreshToken(): Observable<any> {
  const refresh_token = localStorage.getItem("refresh_token");
  console.log("BEFORE REFRESH: " + localStorage.getItem("access_token"));

  return this.http.post(`${apiUrl}token/refresh/`, {"refresh" : refresh_token}).pipe(
    tap(res => {
      const access_token = res["access"];
      const expiresAt = this.tokenExpiresAt(access_token);
      localStorage.setItem("access_token", access_token);
      localStorage.setItem("expires_at", JSON.stringify(expiresAt.valueOf()));
      console.log("AFTER REFRESH: " + localStorage.getItem("access_token"));
    })
  );
}

2. 修改拦截器逻辑

使用RxJS的switchMap操作符,等待refreshToken的Observable执行完成后,再携带新token克隆并发送请求:

constructor(private authService: AuthService) {}

intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
  let access_token = localStorage.getItem("access_token");

  if (access_token) {
    // 检测token是否失效
    if (this.authService.isLoggedOut()) {
      // 等待token刷新完成后再处理请求
      return this.authService.refreshToken().pipe(
        switchMap(() => {
          // 获取刷新后的新token
          const newAccessToken = localStorage.getItem("access_token");
          const clonedRequest = request.clone({
            headers: request.headers.set("Authorization", "Bearer " + newAccessToken)
          });
          return next.handle(clonedRequest);
        }),
        // 处理刷新失败场景(比如refresh token过期)
        catchError(() => {
          // 可添加跳登录页、清除本地存储等逻辑
          localStorage.removeItem("access_token");
          localStorage.removeItem("refresh_token");
          return next.handle(request);
        })
      );
    } else {
      // token有效,直接携带token发送请求
      const cloned = request.clone({
        headers: request.headers.set("Authorization", "Bearer " + access_token)
      });
      return next.handle(cloned);
    }
  } else {
    return next.handle(request);
  }
}

关键说明

  • tap操作符用于在不修改Observable流的前提下执行副作用(比如更新本地存储的token)
  • switchMap确保等待刷新请求完成后再发送原请求,避免异步顺序混乱
  • catchError处理刷新失败的情况,防止因refresh token过期导致请求挂起

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 09:31:14