catchError上的拦截器中的无限循环

时间:2019-09-15 17:41:32

标签: angular

我有一个jwt / refresh令牌授权。

/api/auth/token/refresh由于刷新令牌不存在/过期而导致BadRequest失败时,我试图通过catchError捕获错误,而不是在控制台日志中抛出错误做next.handle(request)以消除错误。当前,只要catchError出现,它就会无限循环运行。换句话说,发现错误后,我要logout()个用户,该用户自己执行一个HTTP发布请求,然后导航到登录页面。

我之所以return this.authService.logout()是因为我在logout()中有一个HTTP发布请求,如果我不以某种方式订阅,它将无法执行。

auth.interceptor.ts

import { Injectable } from '@angular/core';
import {
  HttpEvent,
  HttpInterceptor,
  HttpHandler,
  HttpRequest,
  HttpErrorResponse
} from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError, switchMap } from 'rxjs/operators';
import { Router } from '@angular/router';

import { AuthService } from '../services/auth.service';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
  constructor(
    private router: Router,
    private authService: AuthService) { }

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(request)
      .pipe(
        catchError((error: HttpErrorResponse) => {

          if (error instanceof HttpErrorResponse) {
            if (error.status === 401) {
              return this.handleHttpResponseError(request, next);
            }
          }

          return throwError(error);
        })
      );
  }

  private handleHttpResponseError(request: HttpRequest<any>, next: HttpHandler) {
    const accessToken = this.authService.getJwtToken();

    // if jwt token is not set, we just let the request execute
    if (!accessToken) {
      return next.handle(request);
    }

    // if jwt token has not expired yet, we add the authorize header
    // otherwise we refresh the token
    if (!this.authService.isTokenExpired()) {
      // add "Authorization: Bearer token" to the current request's headers
      return next.handle(this.attachTokenToRequest(request, accessToken));
    } else {
      // when the JWT token expires
      return this.authService.refreshToken()
        .pipe(
          switchMap(token => {
            console.log('Token refreshed.');
            return next.handle(this.attachTokenToRequest(request, token));
          }),
          catchError(error => {
            console.log('INFINITE LOOP HERE.');

            return this.authService.logout()
              .pipe(
                switchMap(error2 => {
                  console.log(error2);
                  this.router.navigate['/log-in'];
                  return next.handle(request);
                })
              );
          })
        );
    }
  }

  private attachTokenToRequest(request: HttpRequest<any>, token: string) {
    return request.clone({
      setHeaders: {
        Authorization: `Bearer ${token}`
      }
    });
  }
}


// auth.service.ts
logout() {
    return this.httpClient.post<AccessToken>(`${this.actionUrl}/token/revoke`, { 'refreshToken': this.getRefreshToken() })
      .pipe(
        map(() => {
          console.log('revoked token');

          localStorage.removeItem(this.JWT_TOKEN);
          localStorage.removeItem(this.REFRESH_TOKEN);
          return true;
        })
      );
}

1 个答案:

答案 0 :(得分:0)

我面对了这个问题,最终发现这是由于路由规则所致!

发生问题是因为我在app-routing.module.ts中设置了onSameUrlNavigation: 'reload',所以一旦我从HttpErrorInterceptor类将页面重定向到登录页面,拦截器就会陷入无限循环。

因此,我们有两种选择来防止发生此无限循环:

  • 删除onSameUrlNavigation: 'reload'

  • 通过检查是否已经是登录页面来防止发生多次重定向: 因此,在http-error.interceptor.ts文件中,您可以导入Router并使用以下条件:

    if(this.router.url != '/login')
       this.router.navigate(["login"]);
    

它对我有用,希望它能对其他人有所帮助。