令牌拦截器不再发送原始请求(Angular、Node.js)

Posted

技术标签:

【中文标题】令牌拦截器不再发送原始请求(Angular、Node.js)【英文标题】:Token interceptor does not send the original request again (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过期后我必须按两次按钮才能获取数据)。

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

感谢您的帮助。

【问题讨论】:

【参考方案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,看看它什么时候执行失败。

以上是关于令牌拦截器不再发送原始请求(Angular、Node.js)的主要内容,如果未能解决你的问题,请参考以下文章

防止在 Angular 中多次触发令牌刷新请求

令牌刷新后Angular 4拦截器重试请求

Angular 4 和 OAuth - 拦截 401 响应,刷新访问令牌并重试请求

Angular JWT 拦截器切换承载令牌以进行刷新

Angular 6在令牌更新后重制请求之前更改JWT令牌

AngularJS - 处理刷新令牌?