Angular14中JWT刷新后新令牌存入lazyUpdate而非Headers的问题
问题分析与解决方案
核心问题
你遇到的两个现象中,lazyUpdate是Angular HttpHeaders的正常内部实现,并非问题根源;真正导致请求未重发的原因是:拦截器的catchError逻辑中,调用handleRefToken后没有返回其生成的Observable,原请求直接抛出错误终止,重发逻辑并未被订阅执行。
1. 关于lazyUpdate的说明
Angular的HttpHeaders是不可变对象,调用set方法时不会直接修改原对象的headers属性,而是将更新操作存入lazyUpdate数组。当实际发送请求时,Angular会合并这些延迟更新,生成最终的请求头。所以你在控制台看到的lazyUpdate是正常现象,实际请求会正确携带Authorization头,无需纠结这个字段。
2. 修复请求重发逻辑
方案一:用RxJS操作符替代async/await(推荐,符合Angular响应式风格)
修改authService的refreshToken方法(返回Observable而非Promise)
public refreshToken(): Observable<Token> { const token = localStorage.getItem('refreshtoken'); const header = new HttpHeaders().set( 'Authorization', 'Bearer ' + token! ); return this.http.get<Token>(`${this.url}/authentication/refresh`, { headers: header, }); }
修改拦截器的handleRefToken方法
handleRefToken(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { const refreshtoken = localStorage.getItem('refreshtoken'); if (!refreshtoken) { return throwError(() => 'BAC'); } return this.authService.refreshToken().pipe( switchMap((newTokens) => { // 更新本地存储的令牌 localStorage.setItem('accesstoken', newTokens.Tokens.accessToken); localStorage.setItem('refreshtoken', newTokens.Tokens.refreshToken); // 克隆请求并添加新令牌 const authRequest = this.addTokenHeader(req, newTokens.Tokens.accessToken); // 重发请求 return next.handle(authRequest); }) ); }
修改拦截器的intercept方法中的catchError逻辑
intercept( req: HttpRequest<any>, next: HttpHandler ): Observable<HttpEvent<any>> { const accesstoken = this.authService.getAccessToken(); const accessTokenExpired = this.authService.accessTokenExpired(); let authRequest = req; if (accesstoken && !accessTokenExpired) { authRequest = this.addTokenHeader(req, accesstoken); } return next.handle(authRequest).pipe( catchError((error: HttpErrorResponse) => { if (error.status === 401) { // 返回重发请求的Observable,替换原错误流 return this.handleRefToken(req, next); } return throwError(() => error); }) ); }
方案二:保留async/await,需将Promise转换为Observable
如果坚持使用async/await,需修改拦截器的catchError逻辑,将handleRefToken返回的Promise转换为Observable:
修改handleRefToken方法
async handleRefToken(req: HttpRequest<any>, next: HttpHandler): Promise<HttpEvent<any>> { const refreshtoken = localStorage.getItem('refreshtoken'); if (refreshtoken) { this.newTokens = await this.authService.refreshToken(); localStorage.setItem('accesstoken', this.newTokens.Tokens.accessToken); localStorage.setItem('refreshtoken', this.newTokens.Tokens.refreshToken); const authRequest = this.addTokenHeader(req, this.newTokens.Tokens.accessToken); // 将Observable转换为Promise,适配async函数返回值 return next.handle(authRequest).toPromise(); } return Promise.reject('BAC'); }
修改拦截器的catchError逻辑
catchError((error: HttpErrorResponse) => { if (error.status === 401) { // 将Promise转换为Observable返回 return from(this.handleRefToken(req, next)); } return throwError(() => error); })
验证修改效果
修复后,当401错误触发时:
- 拦截器会调用刷新令牌接口获取新令牌
- 更新本地存储的令牌
- 克隆原请求并携带新令牌重发
- 重发请求的响应会正常返回给业务代码
内容的提问来源于stack exchange,提问作者M Nouman
相关产品推荐
相关产品推荐

