【问题标题】:Token interceptor does not send the original request again (Angular, Node.js)令牌拦截器不再发送原始请求(Angular、Node.js)
【发布时间】:2020-03-15 04:54:25
【问题描述】:

我正在尝试在我的 Angular 前端实现令牌拦截器,但我当前的实现存在一个问题。

我的拦截器如下所示:

  intercept(request: HttpRequest<any>,next: HttpHandler): Observable<HttpEvent<any>> {
    if (request.headers.get("No-Auth") === "True") {
      return next.handle(request);
    }

    return next.handle(request).pipe(
      catchError(error => {
        if (error instanceof HttpErrorResponse && error.status === 401) {
          this.handle404Error(request, next);
        } else {
          return throwError(error);
        }
      })
    );
  }

及handle404Error函数:

  handle404Error(request: HttpRequest<any>, next: HttpHandler) {
    if (!this.isRefreshing) {
      this.isRefreshing = true;
      this.refreshTokenSubject.next(null);

      return this.authService.refreshToken().subscribe(
        (res: any) => {
          if (Boolean(res) === true) {
            this.isRefreshing = false;
            this.refreshTokenSubject.next(res);
            return next.handle(request);
          } else {
            return this.authService.logout();
          }
        },
        err => {
          return this.authService.logout();
        },
        () => {
          this.isRefreshing = false;
        }
      );
    } else {
      return this.refreshTokenSubject.pipe(
        filter(res => res != null),
        take(1),
        switchMap(() => {
          return next.handle(request);
        })
      );
    }
  }

当我进行 API 调用和服务器返回 404 时,我的拦截器将调用 /token 端点以获取新的 JWT。

请注意,我将 JWT 存储在服务器(内存中)的会话对象中,而在客户端我只有 sessionID 和刷新令牌作为 HttpOnly cookie。

问题是,当我进行 API 调用并且 JWT 无效时,它会设置新的 JWT,但它不会再次发送之前的请求。 (JWT过期后我必须按两次按钮才能获取数据)。

你有什么想法,如何实现这个逻辑?

感谢您的帮助。

【问题讨论】:

    标签: javascript angular rxjs


    【解决方案1】:

    第一件事是不对的——你在拦截器中subscribe,所以你得到了嵌套订阅,因为 Angular 在内部订阅了你的请求。

    您真正想要的是从catchError 返回您的return next.handle(request);。示例:

    return next.handle(request).pipe(
      catchError(error => {
        if (error instanceof HttpErrorResponse && error.status === 401) {
          return this.handle404Error(request, next);
        } else {
          return throwError(error);
        }
      })
    );
    

    handle404Error(request: HttpRequest<any>, next: HttpHandler) {
      if (!this.isRefreshing) {
        this.isRefreshing = true;
        this.refreshTokenSubject.next(null);
    
        return this.authService.refreshToken().pipe(
          mergeMap((res: any) => {
            if (Boolean(res) === true) {
              this.isRefreshing = false;
              this.refreshTokenSubject.next(res);
              return next.handle(request);
            } else {
              /* return */ this.authService.logout();
              return of(undefined) // added this because I dont know what this.authService.logout() returns. It must be an Observable
            }
          }),
          catchError((err) => {
            /* return */ this.authService.logout();
            return throwError(err);
          }))      
      } else {
        return this.refreshTokenSubject.pipe(
          filter(res => res != null),
          take(1),
          switchMap(() => {
            return next.handle(request);
          })
        );
      }
    }
    

    请注意,我没有检查任何错别字。我也丢弃了这个块:

    () => {
      this.isRefreshing = false;
    }
    

    如果您想在this.authService.refreshToken() 完成时执行它,您可以在mergeMapcatchError 旁边添加finalize 运算符。

    解释这里实际发生的事情很简单——你从intercept方法返回一个 Observable。只需将所有 API 调用连接在一起,返回的流就会执行它们。

    【讨论】:

    • 它正在工作!但是有一个小问题 - 当我登录并调用受保护的路由时,它会返回一个结果,但是在令牌过期后我再次单击,调用 /token 不会开始,但是在我刷新页面并单击按钮后,它按我的预期工作。
    • 嗯。也许它通过了这个声明 - request.headers.get("No-Auth") === "True"。这里不能说太多。尝试放一些console.logs,看看它什么时候执行失败。
    猜你喜欢
    • 1970-01-01
    • 2019-04-22
    • 2017-12-25
    • 1970-01-01
    • 1970-01-01
    • 2014-12-20
    • 1970-01-01
    • 1970-01-01
    • 2015-02-01
    相关资源
    最近更新 更多