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
相关产品推荐
相关产品推荐

