如果由于访问令牌过期而导致多个 API 调用未经授权,如何避免多个刷新令牌调用

问题描述 投票:0回答:1

我正在尝试使用 JWT 访问和刷新令牌为存档网站实施身份验证。在刷新令牌调用期间,它会进行一定程度的身份验证,但效率不高,如下图所示,当存在多个 API 调用时,刷新令牌也会被多次调用,最后一个将被验证并刷新会话。我想避免,在获得身份验证后只进行一次刷新调用,然后希望它继续进行 API 调用。

Multiple calls pic

下面是我现在尝试的代码,关于结构,我有一个身份验证拦截器,它拦截请求并在后端添加用于身份验证的标头。另一个错误拦截器会拦截 Http 错误,如果发生 401 错误,它会拦截它并调用刷新函数来处理刷新令牌策略。

身份验证拦截器:

@Injectable()
export class AuthInterceptor implements HttpInterceptor {

  constructor(private storage: StorageService, private authService: AuthService) { }

  intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
    const storedTokens = this.authService.getTokens()    
    if (storedTokens.access_token && !request.url.includes('/auth/refresh') ) {
      const cloned = request.clone({
        headers: request.headers.set("Authorization", storedTokens.access_token)
      });
      return next.handle(cloned);
    }
    else if (storedTokens.refresh_token && request.url.includes('/auth/refresh')){
      const cloned = request.clone({
        headers: request.headers.set("Authorization", storedTokens.refresh_token)
      });
      return next.handle(cloned);
    }
    else {
      return next.handle(request);
    }
  }
}

错误拦截器:

@Injectable()
export class ErrorInterceptor implements HttpInterceptor {
  constructor(private router: Router, private authService: AuthService) { }

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

    return next.handle(request).pipe(
      catchError((error: HttpErrorResponse) => {
        if (error.status === 401) {
          // Access token has expired, attempt to refresh token
          return this.authService.refresh().pipe(
            switchMap(() => {
              const storedTokens = this.authService.getTokens()
              const updatedRequest = request.clone({
              headers: request.headers.set("Authorization", `${storedTokens.access_token}`)
              });
              return next.handle(updatedRequest);
            }),
            catchError((refreshError: any) => {
              // Token refresh failed or refresh token is invalid
              // Redirect user to login page
              this.authService.logout();
              return throwError(refreshError);
            })
          );
        } else if (error.status === 403) {
          // Unauthorized, redirect to login page
          this.authService.logout();
        }
        return throwError(() => new Error(error.message));
      })
    );
  }
}
angular jwt nestjs refresh-token angular16
1个回答
0
投票

我认为这是因为令牌已过期并且组件仍然访问了多个 API(不是 auth API)。 因此,在收到 401 错误后,拦截器将命中 API 以获取刷新令牌,同时组件会命中 ngOnInit 上的多个 API。

对于解决方案,我认为你可以尝试两种选择:

  • 首先,创建一个全局状态来验证或检查令牌,这意味着未过期且有效。然后包装hit API的函数,只有在token有效的情况下才执行该函数。

      getProductList(): void {
       if (isValidToken) {
         this.serviceName.hitApi().subscribe...;
        }
      }
    
  • 其次,将hit API的功能分配给订阅类型的变量,并取消订阅取消网络上重复的HTTP调用,例如:

      getProductList(): void {
       this.$subscription?.unsubscribe()
       this.$subscription = this.serviceName.hitApi().subscribe...;
      }
    

希望可以帮到你。谢谢你。

© www.soinside.com 2019 - 2024. All rights reserved.