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

Angular中用Refresh Token获取新Token后无法重发原请求问题

问题分析

当前错误拦截器捕获401并刷新token后,仅更新了localStorage中的token,但未重新发起原请求,而是直接返回错误,导致前端仍接收到401响应。必须手动刷新页面让jwtInterceptor重新读取新token,该逻辑存在明显缺陷。

修正方案

1. 改造AuthService的refreshAccessToken方法

将回调模式改为返回Promise,方便拦截器等待token刷新完成:

@Injectable({
  providedIn: 'root'
})
export class AuthService {
  constructor(
    private httpClient: HttpClientService,
    private jwtHelper: JwtHelperService,
    private router: Router
  ) {}

  async refreshAccessToken(): Promise<boolean> {
    const refreshToken = localStorage.getItem('refreshToken');
    if (!refreshToken) return false;

    const response$ = this.httpClient.post({
      controller: "auth",
      action: "LoginRefreshToken",
    }, { refreshToken });

    try {
      const tokens: TokenResponse = await firstValueFrom(response$) as TokenResponse;
      if (tokens) {
        localStorage.setItem('accessToken', tokens.accessToken);
        localStorage.setItem('refreshToken', tokens.refreshToken);
        return true;
      }
      return false;
    } catch {
      return false;
    }
  }

  addJwtBearer(req: HttpRequest<unknown>): HttpRequest<unknown> {
    let authToken = localStorage.getItem("accessToken");
    const apiUrl = req.url.startsWith("https://localhost:7096/api");
    if (authToken && apiUrl) {
      return req.clone({
        setHeaders: {
          Authorization: `Bearer ${authToken}`
        }
      });
    }
    return req;
  }

  logOut() {
    localStorage.removeItem('accessToken');
    localStorage.removeItem('refreshToken');
    this.router.navigate(["/"]);
  }
}

2. 修改错误拦截器,实现请求重发

捕获401后,等待token刷新成功,克隆原请求并添加新token重新发起:

export const errorHandlerInterceptor: HttpInterceptorFn = (req, next) => {
  const alertifyService = inject(AlertifyService);
  const router = inject(Router);
  const authService = inject(AuthService);

  return next(req).pipe(
    catchError(async (err) => {
      if (err.status === 401) {
        const hasTokens = !!localStorage.getItem('accessToken') && !!localStorage.getItem('refreshToken');
        if (hasTokens) {
          const refreshSuccess = await authService.refreshAccessToken();
          if (refreshSuccess) {
            const newReq = authService.addJwtBearer(req);
            return next(newReq);
          } else {
            authService.logOut();
          }
        } else {
          alertifyService.message("Yetkisiz erişim!!", {
            messageType: MessageType.Error,
            position: SetPosition.TopRight
          });
          router.navigate(["/"]);
        }
      }
      return throwError(() => err);
    })
  );
};

3. 确保拦截器顺序正确

在provideHttpClient配置中,jwtInterceptor必须在errorHandlerInterceptor之前,保证重发的请求能先带上新token:

// app.config.ts 或相关配置文件
export const appConfig: ApplicationConfig = {
  providers: [
    provideHttpClient(withInterceptors([jwtInterceptor, errorHandlerInterceptor])),
    // 其他服务提供者...
  ]
};
关键说明
  • 刷新token后必须重新发起原请求,确保新token被注入请求头
  • 用Promise/async-await处理异步刷新逻辑,避免回调导致的时序混乱
  • 拦截器顺序直接影响重发请求的token注入逻辑,不可颠倒

内容的提问来源于stack exchange,提问作者serhat başoğlu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 09:20:13