Angular拦截器处理401令牌失效:多请求重复刷新且未用新令牌
问题解决:Angular拦截器中避免重复刷新令牌并正确重试请求
现有代码的问题
- 触发401/403的每个请求都会独立发起刷新令牌请求,造成重复调用
- 原请求没有使用刷新后的新令牌重新发起,直接抛出错误
async/await与RxJS Observable结合不当,拦截器未正确返回重试流
修正方案
首先在AuthenticationService中添加共享的刷新令牌Observable,防止并发请求:
@Injectable({ providedIn: 'root' }) export class AuthenticationService { private refreshToken$: Observable<any> | null = null; // 其他原有代码... refreshToken(): Observable<any> { // 已有刷新请求在进行时,直接返回现有流 if (this.refreshToken$) { return this.refreshToken$; } // 发起刷新请求并维护流状态 this.refreshToken$ = this.http.post('/api/refresh-token', {}) .pipe( tap((response) => { // 更新本地存储的新令牌 this.tokenValue = response.accessToken; }), finalize(() => { // 请求完成后清空刷新流标记 this.refreshToken$ = null; }), catchError((err) => { // 刷新失败时清空令牌,避免无限循环 this.tokenValue = null; this.refreshToken$ = null; return throwError(() => err); }) ); return this.refreshToken$; } }
然后修改ErrorInterceptor,正确处理令牌刷新与请求重试逻辑:
@Injectable() export class ErrorInterceptor implements HttpInterceptor { constructor( private authenticationService: AuthenticationService, private headerRequestService: HeaderRequestService ) { } intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { return next.handle(request).pipe( catchError((err) => { // 仅处理401/403且当前存在有效令牌的场景 if ([401, 403].includes(err.status) && this.authenticationService.tokenValue) { // 刷新令牌后重试原请求 return this.authenticationService.refreshToken().pipe( switchMap(() => { // 克隆原请求并添加新令牌头 const newRequest = this.headerRequestService.addHeader(request); return next.handle(newRequest); }), catchError((refreshErr) => { // 刷新失败时抛出最终错误 const error = (refreshErr?.error?.message) || refreshErr.statusText; console.error(refreshErr); return throwError(() => error); }) ); } // 非令牌过期类错误直接抛出 const error = (err?.error?.message) || err.statusText; console.error(err); return throwError(() => error); }) ); } }
关键改进点
- 避免重复刷新:通过共享的
refreshToken$确保同一时间仅发起一个刷新请求 - 正确重试请求:用
switchMap衔接刷新流与原请求重试逻辑,确保使用新令牌 - 完善错误处理:刷新失败时清空令牌,避免无限触发刷新逻辑
- 规范异步流处理:移除不合适的
async/await,统一用RxJS操作符处理异步流程
内容的提问来源于stack exchange,提问作者kareem nagy
相关产品推荐
相关产品推荐

