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

如何避免Access Token过期时重复调用Refresh Token并重发401请求

解决Angular拦截器中并行401请求重复调用Refresh Token的问题

我基于拦截器实现了Refresh Token逻辑,功能正常,但存在一个关键问题:当多个API并行调用且全部返回401时,Refresh Token会被多次触发调用,次数取决于this.isRefreshing变量设为true的速度。需要实现仅调用一次Refresh Token API,让所有返回401的待处理API在Token刷新后重新发起请求。

调用截图

截图中3个API均返回401,当前逻辑会多次调用Refresh Token,期望仅调用一次,其余请求在Token刷新后自动重试。


修改后的拦截器代码

import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from "@angular/common/http";
import { Injectable, Injector } from "@angular/core";
import { BehaviorSubject, Observable, throwError } from "rxjs";
import { catchError, filter, switchMap, take } from "rxjs/operators";
import { AuthenticationService } from "../services";
import { TokenService } from "../services/token.service";

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
    private isRefreshing = false;
    private tokenService: TokenService;
    private refreshTokenSubject: BehaviorSubject<any> = new BehaviorSubject<any>(null);

    constructor(private injector: Injector) {
        this.tokenService = this.injector.get(TokenService);
    }

    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        // 给请求添加有效Token
        const token = this.tokenService.accessToken;
        if (token) {
            req = this.addTokenHeader(req, token);
        }

        // 携带credentials
        req = req.clone({ withCredentials: true });

        // 排除Refresh Token接口本身,避免拦截循环
        if (req.url.includes('refreshToken')) { // 替换为你的实际Refresh Token接口路径
            return next.handle(req);
        }

        return next.handle(req).pipe(catchError(error => {
            if (error instanceof HttpErrorResponse && error.status === 401) {
                return this.handle401Error(req, next);
            }
            return throwError(error);
        }));
    }

    private handle401Error(request: HttpRequest<any>, next: HttpHandler) {
        const authService = this.injector.get(AuthenticationService);
        
        if (!this.isRefreshing) {
            this.isRefreshing = true;
            this.refreshTokenSubject.next(null);

            return authService.refreshToken().pipe(
                switchMap((token: any) => {
                    this.isRefreshing = false;
                    this.tokenService.accessToken = token.accessToken;
                    this.refreshTokenSubject.next(token.accessToken);
                    // 用新Token重新发起当前请求
                    return next.handle(this.addTokenHeader(request, token.accessToken));
                }),
                catchError((err) => {
                    this.isRefreshing = false;
                    this.refreshTokenSubject.next(null);
                    authService.directRedirectToLogin();
                    return throwError(err);
                })
            );
        }

        // 等待Refresh Token完成,拿到新Token后重新发起请求
        return this.refreshTokenSubject.pipe(
            filter(token => token !== null),
            take(1),
            switchMap(token => next.handle(this.addTokenHeader(request, token)))
        );
    }

    private addTokenHeader(request: HttpRequest<any>, token: string) {
        const setHeaders: any = {
            Authorization: `Bearer ${token}`
        };

        // 非FormData请求添加Content-Type
        if (!(request.body instanceof FormData)) {
            setHeaders['Content-Type'] = 'application/json';
        }

        return request.clone({ setHeaders });
    }
}

关键改动说明

  1. 排除Refresh Token请求拦截
    在intercept方法中添加判断,跳过对Refresh Token接口的拦截,避免因该接口返回401导致的无限循环调用。

  2. 确保所有请求使用新Token重试
    无论是第一个触发Refresh Token的请求,还是等待的并行请求,都通过addTokenHeader方法用新Token克隆请求后再发起,避免使用未携带新Token的原请求。

  3. 优化状态管理
    在Refresh Token请求失败时,重置refreshTokenSubject为null,避免后续请求一直等待无效状态。

  4. 恢复初始Token添加逻辑
    正常请求发起时自动携带当前有效Token,减少不必要的401触发场景。


实现原理

当多个并行请求同时返回401时,第一个请求会将isRefreshing设为true并发起Refresh Token调用;后续所有请求会进入等待分支,监听refreshTokenSubject的状态变化。当Refresh Token成功获取新Token后,refreshTokenSubject会发出新Token值,所有等待的请求都会用新Token重新发起,从而实现仅调用一次Refresh Token,所有请求自动重试的效果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 09:15:32