JWT 令牌刷新后返回重新调用

Posted

技术标签:

【中文标题】JWT 令牌刷新后返回重新调用【英文标题】:Return remaked call after JWT token refresh 【发布时间】:2019-10-10 17:48:14 【问题描述】:

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

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

这个过程必须对用户隐藏。我已经捕获了 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);
    

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

【问题讨论】:

为什么要等待?你的 refreshToken() 是异步的 对,但怎么能强迫它等待呢? 【参考方案1】:

您想返回 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
                ,
              );

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

【讨论】:

我将 switchMap 放在管道中,它似乎可以工作,我正在测试它。谢谢

以上是关于JWT 令牌刷新后返回重新调用的主要内容,如果未能解决你的问题,请参考以下文章

JWT, 为啥需要刷新令牌?

使用带有 JWT Auth/Laravel 的 Angular JS 获取新的刷新令牌后,如何恢复/重新发送请求?

过期后使用刷新令牌获取访问令牌(JWT)

浏览器刷新后丢失 JWT 令牌

在 nodejs 中使用 JWT 刷新令牌的最佳实践

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