API 请求失败,出现 500 错误并无限循环

问题描述 投票:0回答:1

我有一个具有以下功能的 Angular 应用程序:

  • 检索
    访问令牌
    login方法。
  • 一个
    getRefreshToken
    方法,用于检索 刷新令牌
  • A
    JwtInterceptor
    • 将令牌添加到请求中。
    • 如果发生
      tokenInvalid
      错误,触发另一个拦截器调用
      getRefreshToken
      方法。

问题
当API未运行时,应用程序不断尝试循环调用

getRefreshToken
方法来获取新的刷新令牌,我不明白为什么。

身份验证基础设施:

 private auth$: Observable<AuthenticationToken> | null | undefined;

  login(email: string, password: string): Observable<AuthenticationToken> {
    if (!this.auth$) {
      const body = new HttpParams()
        .set('username', email)
        .set('password', password);

      const headers = new HttpHeaders({
        'Content-Type': 'application/x-www-form-urlencoded',
      });

      this.auth$ = this.httpClient
        .post<AuthenticationToken>(
          'http://localhost:8080/realms/master/protocol/openid-connect/token/',
          body.toString(),
          { headers }
        )
        .pipe(shareReplay(1));
    }
    return this.auth$;
  }

  refreshToken() {
    return from(this.storage.get('refresh_token')).pipe(
      switchMap((storedRefreshToken) => {
        const body = new HttpParams()
          .set('grant_type', 'refresh_token')
          .set('refresh_token', storedRefreshToken);

        const headers = new HttpHeaders().set(
          'Content-Type',
          'application/x-www-form-urlencoded'
        );

        return this.httpClient
          .post<AuthenticationToken>(
            'http://localhost:8080/master/clovis/protocol/openid-connect/token/',
            body,
            { headers }
          )
          .pipe(shareReplay(1));
      })
    );
  }

Jwt拦截器:

export function jwtInterceptor(request: HttpRequest<any>, next: HttpHandlerFn) {
  const storage = inject(Storage);

  return from(storage.get('access_token')).pipe(
    switchMap((token) => {
      const newRequest = request.clone({
        setHeaders: {
          Authorization: 'Bearer ' + token,
        },
      });
      return next(newRequest);
    })
  );
}

export const refreshInterceptor: HttpInterceptorFn = (req, next) => {
  const authInfra = inject(AuthenticationInfrastructure);
  const localStorage = inject(LocalStorageAuthenticationInfrastructure);
  const authApp = inject(AuthenticationApplication);

  return next(req).pipe(
    catchError((error: HttpErrorResponse) => {
      if (error.status === 401 || error.status === 0) {
        return authInfra.refreshToken().pipe(
          switchMap((tokenResponse: WithToken) => {
            localStorage.startSession({
              access_token: tokenResponse.access_token,
              refresh_token: tokenResponse.refresh_token,
            });
            const newAuthReq = req.clone({
              headers: req.headers.set(
                'Authorization',
                `Bearer ${tokenResponse.access_token}`
              ),
            });
            return next(newAuthReq);
          }),
          catchError((refreshError) => {
            console.error('Error refreshing token', refreshError);
            authApp.logout();
            return throwError(() => new Error('Token refresh failed!'));
          })
        );
      } else {
        return throwError(() => error);
      }
    })
  );
};

我的浏览器控制台:

user.application.ts:12 
 POST http://localhost:8080/master/clovis/protocol/openid-connect/token/ net::ERR_CONNECTION_REFUSED
user.application.ts:12 
 POST http://localhost:8080/master/clovis/protocol/openid-connect/token/ net::ERR_CONNECTION_REFUSED
user.application.ts:12 
 POST http://localhost:8080/master/clovis/protocol/openid-connect/token/ net::ERR_CONNECTION_REFUSED
user.application.ts:12 
 POST http://localhost:8080/master/clovis/protocol/openid-connect/token/ net::ERR_CONNECTION_REFUSED
user.application.ts:12 
 POST http://localhost:8080/master/clovis/protocol/openid-connect/token/ net::ERR_CONNECTION_REFUSED
user.application.ts:12 
 POST http://localhost:8080/master/clovis/protocol/openid-connect/token/ net::ERR_CONNECTION_REFUSED
angular typescript rxjs angular-http-interceptors
1个回答
0
投票

观察代码,主要问题似乎出在

refereshToken()
逻辑上。

根据我所发生的情况,

refreshInterceptor
捕获 401 或 0 错误并尝试刷新令牌。由于 API 已损坏,令牌刷新请求也会失败,从而再次触发拦截器,这可能是无限循环的原因。

我们需要通过处理

refreshToken()
调用失败来停止循环。

尝试在代码中添加以下 catch 片段:

export const refreshInterceptor: HttpInterceptorFn = (req, next) => {
  const authInfra = inject(AuthenticationInfrastructure);
  const localStorage = inject(LocalStorageAuthenticationInfrastructure);
  const authApp = inject(AuthenticationApplication);

  return next(req).pipe(
    catchError((error: HttpErrorResponse) => {
      if (error.status === 401 || error.status === 0) {
        return authInfra.refreshToken().pipe(
          switchMap((tokenResponse: WithToken) => {
            localStorage.startSession({
              access_token: tokenResponse.access_token,
              refresh_token: tokenResponse.refresh_token,
            });
            const newAuthReq = req.clone({
              headers: req.headers.set(
                'Authorization',
                `Bearer ${tokenResponse.access_token}`
              ),
            });
            return next(newAuthReq);
          }),
          catchError((refreshError) => {
            console.error('Error refreshing token', refreshError);
            authApp.logout(); // End the session
            return throwError(() => new Error('Token refresh failed!')); // Prevent further retries
          })
        );
      } else {
        return throwError(() => error);
      }
    })
  );
};

希望这有帮助!

© www.soinside.com 2019 - 2024. All rights reserved.