javascript 使用Angular和HTTP拦截器重新验证Firebase用户令牌

edqdpe6u  于 2023-05-05  发布在  Java
关注(0)|答案(1)|浏览(163)

我在Angular HTTP拦截器代码中使用错误捕获时遇到了一个奇怪的问题。
似乎我的“.then()”语句链中的代码以某种方式被无序地触发。
这是我的代码...

import { Injectable } from '@angular/core';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HTTP_INTERCEPTORS, HttpClient, HttpErrorResponse } from '@angular/common/http';
import { catchError, Observable, retry, switchMap, throwError } from 'rxjs';
import { FirebaseService } from '../services/firebase.service';
import { getAuth } from 'firebase/auth';

@Injectable()
export class HttpRequestInterceptor implements HttpInterceptor {

  //AW-Note: Adding this in here to try the appearance of "Persistent Login"
  static accessToken = '';
  static apiKey = 'API_KEY_HERE';

  constructor(private firebaseService: FirebaseService, private http: HttpClient) { }
  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const token = localStorage.getItem('token');

    function refreshToken(): Promise<string> {
      // api call returns Promise<string>
      const auth = getAuth();
      const myUser = auth.currentUser;
      return myUser!.getIdToken(true);
    }

    async function getNewToken(): Promise<string> {
      const value = await refreshToken() // how to unwrap the value inside this  promise
      return value;
    }


    console.log('Flowing through HTTP Interceptor');

    req = req.clone({
      //If you comment out the below line you will no longer send Credentials with the request  
      withCredentials: true,
      setHeaders: { Authorization: `Bearer ${token}` }
    });

    return next.handle(req).pipe(/*retry(10),*/ catchError((err: HttpErrorResponse) => {
      console.log("ERROR FOUND! CHECKING TO SEE IF IT IS A 401 ERROR NEXT. Error: " + err);
      if (err.status === 401 || err.status === 0) {
        console.log("NEW TOKEN NEEDED - Yo we got that 401 error going on - let's try and refresh that thang");

        //Refresh Token from Firebase

        console.log("Old Token: " + JSON.stringify(token));


        console.log("Attempting to refresh token now...");

        getNewToken()
          .then((newToken) => {
            console.log('New Token: ' + JSON.stringify(newToken));
            localStorage.setItem('token', newToken);
            console.log('Updated Local Storage with new token');
          })
          .then(() => {
            console.log("Here is where the request will refresh");
            return next.handle(req.clone({
              withCredentials: true,
              setHeaders: {
                Authorization: `Bearer ${localStorage.getItem('token')}`
              }
            }))
          })
          .catch((error) => {
            console.log("Error in Getting new token... Error: " + JSON.stringify(error));
          });
      }
      return throwError(() => err);
    }))
  }
}

export const httpInterceptorProviders = [
  { provide: HTTP_INTERCEPTORS, useClass: HttpRequestInterceptor, multi: true },
];

因此,一旦localStorage中的令牌过期,代码就成功地检测到错误,并且在第一个“pipe()”块中捕获了它。
它成功地从Firebase获得了一个新令牌,但是当它向下移动到最后的“then”语句以使用新令牌重试API调用时,似乎在控制台日志语句“正在尝试刷新令牌...”之后立即调用了API,而不是像我期望的那样“这里是请求将刷新的地方”。
这是控制台的输出,所以你可以看到我在说什么...

Flowing through HTTP Interceptor

http.interceptor.ts:35
ERROR FOUND! CHECKING TO SEE IF IT IS A 401 ERROR NEXT. Error: [object Object]

http.interceptor.ts:44
NEW TOKEN NEEDED - Yo we got that 401 error going on - let's try and refresh that thang

http.interceptor.ts:46
Old Token: "eyJhbGciOiJSUzI1NiIsImtpZCI6ImU3OTMwMjdkYWI0YzcwNmQ2ODg0NGI4MDk2ZTBlYzQzMjYyMjIwMDAiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vdHV0b3JpYWwtYXBwLTEtODEyNDkiLCJhdWQiOiJ0dXRvcmlhbC1hcHAtMS04MTI0OSIsImF1dGhfdGltZSI6MTY4MjY4NTk3NCwidXNlcl9pZCI6ImUwUXdEYmZhVlpSdWZkaGF6S1FjdDU5ajdQVTIiLCJzdWIiOiJlMFF3RGJmYVZaUnVmZGhhektRY3Q1OWo3UFUyIiwiaWF0IjoxNjgyNzkwNjk4LCJleHAiOjE2ODI3OTQyOTgsImVtYWlsIjoiYWxleHdyaWdodDkyM0BnbWFpbC5jb20iLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsImZpcmViYXNlIjp7ImlkZW50aXRpZXMiOnsiZW1haWwiOlsiYWxleHdyaWdodDkyM0BnbWFpbC5jb20iXX0sInNpZ25faW5fcHJvdmlkZXIiOiJwYXNzd29yZCJ9fQ.eaPLxmi-wtsDV941M7A0ouMJhraDhKVbd_EFmdnFH3HZSCHRK4K4HRFZZ6RrvRo0FOd60NZLstE-MXzXumb7N6J6LOWqE_pUrtsylqDE15Wp4hJmcHP8HZmJA4wve3JlLox_i3bNgzY7F4NTuGsij92nZd1qa-uDd6_SI4o2ABtZGGfX-11RAUKN7sjGRTy_zMM2ielb8im7W8q8KbdW1OxISqJDE4rbqyBlLBbUZWI9z1RMRpxD_s6k6y3bZfYTq_seFWaKLH3rEMfMGujS4x_ytlHqWaLbU7v3BXRxhZJsgui3r1ktQROFZdJwApybQ8-tpeEZwp0KDdnx2cautA"

http.interceptor.ts:51
Attempting to refresh token now...

HttpErrorResponse {headers: HttpHeaders, status: 0, statusText: 'Unknown Error', url: 'http://localhost:8080/api/tutorials', ok: false, …}

add-tutorial.component.ts:59
New Token: "eyJhbGciOiJSUzI1NiIsImtpZCI6ImU3OTMwMjdkYWI0YzcwNmQ2ODg0NGI4MDk2ZTBlYzQzMjYyMjIwMDAiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vdHV0b3JpYWwtYXBwLTEtODEyNDkiLCJhdWQiOiJ0dXRvcmlhbC1hcHAtMS04MTI0OSIsImF1dGhfdGltZSI6MTY4MjY4NTk3NCwidXNlcl9pZCI6ImUwUXdEYmZhVlpSdWZkaGF6S1FjdDU5ajdQVTIiLCJzdWIiOiJlMFF3RGJmYVZaUnVmZGhhektRY3Q1OWo3UFUyIiwiaWF0IjoxNjgyODc3MjIwLCJleHAiOjE2ODI4ODA4MjAsImVtYWlsIjoiYWxleHdyaWdodDkyM0BnbWFpbC5jb20iLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsImZpcmViYXNlIjp7ImlkZW50aXRpZXMiOnsiZW1haWwiOlsiYWxleHdyaWdodDkyM0BnbWFpbC5jb20iXX0sInNpZ25faW5fcHJvdmlkZXIiOiJwYXNzd29yZCJ9fQ.OoykyUgJdb6ugVaiJClcQjV6DpKQAQCl2Jx1VwHSrS77NzN5F0bfRCTHAS-wvXYgbzhAGw39fwq67HXoC74OdugKbJpnhpyf3e-TWLdN1SD195aBedPvB6KtApD7nj4WPTlYuE8mupcqXB2mw8NvDuxyZLV4pOv4A7ou5J7mJkrwiGU0WboM5HPz7LeoXP5QlxpPZ8dQ4f2cnJ3AKD9SFSgrojhi1tgrOh80YCsq5ZP46VXPj4ym-MchQ4T4rQh6wjZ9NllzngM0-QvZD4Bz9m52g5hBaICqVQnVpbzX_xMnixZYI7N9LGC3F-GXEeLF-IhLgdEbcM8vL7LypOZyPQ"

http.interceptor.ts:59
Updated Local Storage with new token

http.interceptor.ts:61
About to retry the API Call now

http.interceptor.ts:65
Here is where the request will refresh

这里的任何帮助都将不胜感激。我的.then()语句有问题吗?对 typescript 、角化和像这样的promise处理来说相当陌生。谢谢。
我已经尝试过在.then()语句周围使用一些语句,但是我认为return语句看起来似乎执行得 * 早 *
(the HTTP请求在日志显示“HttpErrorResponse”的位置发出)
提前感谢!

wsxa1bj1

wsxa1bj11#

这是最终为我工作的解决方案。
主要的两个变化是:
1.使用from(refreshToken())将refreshToken()返回的promise转换为一个observable,该observable可以通过管道传输到新的switchMap操作符中。
1.将重试逻辑移动到switchMap操作符中,以便它仅在获得新令牌并将其添加到本地存储后重试API调用。

import { Injectable } from '@angular/core';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HTTP_INTERCEPTORS, HttpClient, HttpErrorResponse } from '@angular/common/http';
import { catchError, Observable, retry, switchMap, throwError } from 'rxjs';
import { FirebaseService } from '../services/firebase.service';
import { getAuth } from 'firebase/auth';

@Injectable()
export class HttpRequestInterceptor implements HttpInterceptor {

  //AW-Note: Adding this in here to try the appearance of "Persistent Login"
  static accessToken = '';
  static apiKey = 'API_KEY_HERE';

  constructor(private firebaseService: FirebaseService, private http: HttpClient) { }
  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const token = localStorage.getItem('token');

    function refreshToken(): Promise<string> {
      // api call returns Promise<string>
      const auth = getAuth();
      const myUser = auth.currentUser;
      return myUser!.getIdToken(true);
    }

    async function getNewToken(): Promise<string> {
      const value = await refreshToken() // how to unwrap the value inside this  promise
      return value;
    }


    console.log('Flowing through HTTP Interceptor');

    req = req.clone({
      //If you comment out the below line you will no longer send Credentials with the request  
      withCredentials: true,
      setHeaders: { Authorization: `Bearer ${token}` }
    });

    return next.handle(req).pipe(/*retry(10),*/ catchError((err: HttpErrorResponse) => {
      console.log("ERROR FOUND! CHECKING TO SEE IF IT IS A 401 ERROR NEXT. Error: " + err);
      if (err.status === 401 || err.status === 0) {
        console.log("NEW TOKEN NEEDED - Yo we got that 401 error going on - let's try and refresh that thang");

        //Refresh Token from Firebase

        console.log("Old Token: " + JSON.stringify(token));


        console.log("Attempting to refresh token now...");

        getNewToken()
          .then((newToken) => {
            console.log('New Token: ' + JSON.stringify(newToken));
            localStorage.setItem('token', newToken);
            console.log('Updated Local Storage with new token');
          })
          .then(() => {
            console.log("Here is where the request will refresh");
            return next.handle(req.clone({
              withCredentials: true,
              setHeaders: {
                Authorization: `Bearer ${localStorage.getItem('token')}`
              }
            }))
          })
          .catch((error) => {
            console.log("Error in Getting new token... Error: " + JSON.stringify(error));
          });
      }
      return throwError(() => err);
    }))
  }
}

export const httpInterceptorProviders = [
  { provide: HTTP_INTERCEPTORS, useClass: HttpRequestInterceptor, multi: true },
];

相关问题