首页 > 解决方案 > 刷新 JWT 令牌后返回重新调用

问题描述

我正在使用 JWT,我有这个逻辑:

  1. 进行 http 调用
  2. 如果令牌过期或结果返回 401
  3. 返回 401 时,我必须进行 http 调用以要求新令牌
  4. 使用新令牌重新进行初始调用
  5. 返回结果

这个过程必须对用户隐藏。我已经捕获了 401 状态码并在检索新令牌后重复了原始调用,问题是将结果返回给原始调用。这是带有 http 请求的服务:

    getListCategories(){
        return this.http.get<Category[]>("/api/configuration/category").pipe(
          catchError(err =>  this.handleError.handleError(err, { severity: 'error', summary: 'Error retrieving the list of categories', life: 5000 }))
        );
    }

这是进行刷新调用并重复原始调用的错误拦截器:

    export class ErrorInterceptorService implements HttpInterceptor {

      constructor(private auth: AuthService, public router: Router) { }

      intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        const { shouldRetry } = this;
        return next.handle(request).pipe(
          retryWhen(genericRetryStrategy({
            shouldRetry
          })),

          catchError(err => {
            //401 the token is invalid so I have to refresh it
            if (err.status === 401 && request.url !== "/api/login") {
              this.auth.refreshToken().subscribe(
                (apiResult: SessionTokenResponse) => {
                  this.auth.saveToken(apiResult.token);
                  request = request.clone({ headers: request.headers.set('Authorization', 'Bearer ' + apiResult.token) });
                  next.handle(request).subscribe();
                },
              );
            } else if (err.status === 401 && request.url === "/api/login") {
              this.auth.logout()
            }else{
              const error = err.error.message || err.statusText;
              return throwError(error);
            }
          }),
        )
      }
      private shouldRetry = (error) => (error.error instanceof ErrorEvent);
    }

问题出在服务中,它不等待重拍调用,而是在第一个错误后退出。你能帮我吗?谢谢

标签: angularrestjwtobservableangular-http-interceptors

解决方案


您希望返回Observable包含原始请求和重复请求之间的额外请求的链操作。我会尝试使用switchMap.

它会是这样的:

catchError(err => {
            if (err.status === 401 && request.url !== "/api/login") {
              //by returning observable here, you are "chaining" the calls so original caller will get this observable's result. `catchError` can be threated like `catch` block in `try-catch` where you can still do operations and return results - ergo continue operations.
              return this.auth.refreshToken()
                .switchMap( // this just switches from one observable to another
                (apiResult: SessionTokenResponse) => {
                  this.auth.saveToken(apiResult.token);
                  request = request.clone({ headers: request.headers.set('Authorization', 'Bearer ' + apiResult.token) });
                  return next.handle(request); // return original request handler with updated headers
                },
              );

显然没有经过测试,并且可能是此处所写的语法无效。


推荐阅读