angular 2 http异常处理程序和jwt刷新

Posted

技术标签:

【中文标题】angular 2 http异常处理程序和jwt刷新【英文标题】:angular 2 http exception handler and jwt refresh 【发布时间】:2018-01-23 10:21:30 【问题描述】:

当我遇到某个异常时,我正在尝试刷新 JWT 令牌,而当它是另一个异常时,我的 ErrorHandler 应该处理它们。

我有一段代码,一个用于令牌刷新的代码,还有一段用于异常处理程序的代码,但我无法将它们组合在一起。

问题是我不能抛出异常并用我的 ErrorHandler 在 observable 中捕获它。

这是我可以用来刷新令牌的代码。当它失败时,它会检查错误代码是否为token_expired,如果是,它将刷新令牌并重试请求。

export class HttpErrorService extends Http 

  constructor(backend: XHRBackend, defaultOptions: RequestOptions) 
    super(backend, defaultOptions);
  

  request(url: string | Request, options?: RequestOptionsArgs, disableRefresh = false): Observable<Response> 
      return super.request(url, options).catch((error: Response) => 
        // Refresh token on token_expired exception.
        if (!disableRefresh && error.status === 401 && error.json().error.code === 'token_expired') 
          return this.renewToken().flatMap((response) => 
            const res = response.json();
            // Replace the token in storage.
            localStorage.setItem('__token', res.data.token);

            // Replace request the token with the new one.
            if (url instanceof Request) 
              url.headers.set('Authorization', 'Bearer ' + res.data.token);
             else if (options) 
              options.headers.set('Authorization', 'Bearer ' + res.data.token);
            

            // To prevent a loop disable refreshing at the next request.
            return this.request(url, options, true);
          );
        

        // Here I want to throw the exception.
        // I need to be able to catch it with my exception handler.
        // throw error; doesn't work.
        return Observable.throw(error);
      );
  

  private getBaseUrl(): string 
    return environment.base_uri;
  ;

  renewToken(): Observable<Response> 
    const headers = new Headers();
    headers.append('Authorization', 'Bearer ' + localStorage.getItem('__token'))

    return this.post(this.getBaseUrl() + '/auth/refresh', , headers: headers);
  

上面唯一的坏处是我无法在我的异常处理程序中捕获异常。

下面的代码可以抛出可以被 ErrorHandler 捕获的异常。但我不知道如何在一次调用中刷新令牌...

export class HttpErrorService extends Http 

  constructor(backend: XHRBackend, defaultOptions: RequestOptions) 
    super(backend, defaultOptions);
  

  request(url: string | Request, options?: RequestOptionsArgs, disableRefresh = false): Observable<Response> 
    return Observable.create(observer => 
      super.request(url, options).subscribe(
        res => observer.next(res),
        err => 
          if (!disableRefresh && err.status === 401 && err.json().error.code === 'token_expired') 
            // I can't return this.renewToken()...
          
          observer.error(err);
          throw new HttpException(err); // this is getting catched by the ErrorHandler
        ,
        () => observer.complete);
    );
  

  private getBaseUrl(): string 
    return environment.base_uri;
  ;

  renewToken(): Observable<Response> 
    const headers = new Headers();
    headers.append('Authorization', 'Bearer ' + localStorage.getItem('__token'))

    return this.post(this.getBaseUrl() + '/auth/refresh', , headers: headers);
  

我的错误处理程序只包含一个console.log()。https://angular.io/api/core/ErrorHandler

我怎样才能让它工作?

【问题讨论】:

也可以在实际调用请求函数和ErrorHandler的地方加上代码吗? @trungk18 我正在重写 Http 类,所以每个 http 请求都使用 request 函数。它是默认的 Http 库。 ErrorHandler 只是一个console.log(); 嗨 Jan,你能在你的第一个代码块上尝试“throw Observable.throw(error)”而不是“return Observable.throw(error)”吗? @trungk18 它到达订阅错误但它没有到达异常处理程序,我的第二个“代码”确实到达了它们。我使用订阅错误向用户显示错误消息。以及处理错误的异常处理程序。 我不太擅长 rxjs,所以我们可能需要一些专家建议:D 【参考方案1】:

几个小时后,我终于找到了解决方案!

export class HttpErrorService extends Http 

  constructor(backend: XHRBackend, defaultOptions: RequestOptions) 
    super(backend, defaultOptions);
  

  request(url: string | Request, options?: RequestOptionsArgs, disableRefresh = false): Observable<Response> 
    return Observable.create(observer => 
      super.request(url, options).retryWhen(attempts => this.retryRequest(attempts)).catch((error: Response) => 
        // Refresh token on token_expired exception.
        if (!disableRefresh && error.status === 401 && error.json().error.code === 'token_expired') 
          return this.renewToken().flatMap((response) => 
            const res = response.json();
            // Replace the token in storage.
            localStorage.setItem('__token', res.data.token);

            // Replace request the token with the new one.
            if (url instanceof Request) 
              url.headers.set('Authorization', 'Bearer ' + res.data.token);
             else if (options) 
              options.headers.set('Authorization', 'Bearer ' + res.data.token);
            

            // To prevent a loop disable refreshing at the next request.
            return this.request(url, options, true);
          );
        

        throw Observable.throw(error);
      ).subscribe(
        res => observer.next(res),
        err => 
          observer.error(err);
          throw new HttpException(err);
        
      );
    );
  

  private getBaseUrl(): string 
    return environment.base_uri;
  ;

  renewToken(): Observable<Response> 
    const headers = new Headers();
    headers.append('Authorization', 'Bearer ' + localStorage.getItem('__token'))

    return this.post(this.getBaseUrl() + '/auth/refresh', , headers: headers);
  

  retryRequest(attempts: any) 
    let count = 0;

    return attempts.flatMap(error => 
        return ++count >= 3 ? Observable.throw(error) : Observable.timer(count * 1000);
    );
  


【讨论】:

以上是关于angular 2 http异常处理程序和jwt刷新的主要内容,如果未能解决你的问题,请参考以下文章

Django Rest Framework JWT 中的 ValidationError 不使用自定义异常处理程序

带有 Angular 8 和 Springboot 2 的 JWT:JWT 字符串必须恰好包含 2 个句点字符

了解 JWT 和 HTTP 授权标头? (客户端:Angular,服务器:php)

Angular2 JWT 身份验证 - JWT 必须分为 3 个部分?

使用 Angular 和 spring 进行 Jwt 身份验证

JWT 与 Angular 和 SpringBoot