如何在angular 8中将firebase jwt getIdToken发送到http get请求?

isA*_*Aif 1 javascript firebase firebase-authentication angular

我正在使用 angular 8 制作 SPA。

Firebase 用于在客户端和后端对用户进行身份验证,因此我需要将 http.get 请求中的 jwt 令牌发送到后端以对用户进行身份验证。

后端是使用 django 2.2 和 django rest 框架制作的 API,它发送要在客户端应用程序中使用的 api。

auth.service.ts

@Injectable({
  providedIn: 'root'
})

export class AuthService {
  userData: any; // Save logged in user data
  public userToken: string;

  constructor(
    public afs: AngularFirestore,   // Inject Firestore service
    public afAuth: AngularFireAuth, // Inject Firebase auth service
    public router: Router,
    public ngZone: NgZone // NgZone service to remove outside scope warning
  ) {
    /* Saving user data in localstorage when 
    logged in and setting up null when logged out */
    this.afAuth.authState.subscribe(user => {
      if (user) {
        this.userData = user;
        localStorage.setItem('user', JSON.stringify(this.userData));
        JSON.parse(localStorage.getItem('user'));
      } else {
        localStorage.setItem('user', null);
        JSON.parse(localStorage.getItem('user'));
      }
    });

  }


  GetToken(): string {
    this.afAuth.auth.onAuthStateChanged( user => {
      if (user) {
        user.getIdToken().then(idToken => {
          this.userToken = idToken;

            // this shows the userToken
          console.log('token inside getToken method ' + this.userToken);
        });
      }
    });

    // this shows userToken as undefined
    console.log('before return ' + this.userToken);
    return this.userToken;
  }

}

Run Code Online (Sandbox Code Playgroud)

api.service.ts

@Injectable({
  providedIn: 'root'
})
export class ApiService {

  private url = environment.baseUrl;
  token: any;
  data: any;

  constructor(
    private http: HttpClient,
    private authService: AuthService,
    ) {}



    // old method to get emloyees data
  // public getEmployees(): Observable<Employee[]> {
  //   return this.http.get<Employee[]>(`${this.url}/employee/`);
  // }


  httpOptions = {
    headers: new HttpHeaders({
      'Content-Type': 'application/json',
      'Authorization': 'JWT ' + this.authService.GetToken()
    }),
  };


  public getEmployees(): Observable<Employee[]> {

      // token is undefined here
    console.log('token inside getEmployees method ' + this.token);

    return this.http.get<Employee[]>(`${this.url}/employee/`, this.httpOptions);
  }

}
Run Code Online (Sandbox Code Playgroud)

后端工作正常,我通过在 httpOptions 中添加令牌进行了验证,如下所示:

httpOptions = {
    headers: new HttpHeaders({
      'Content-Type': 'application/json',
      'Authorization': 'JWT ' + 'ey.....'
    }),
  };
Run Code Online (Sandbox Code Playgroud)

但是当我尝试按照代码中给出的方法进行操作时,它不起作用。用户令牌保持未定义。

Fra*_*len 6

彼得的回答有关键所在:getIdToken()是异步的,所以当你return this.userToken;运行时,this.userToken = idToken;还没有运行。您应该能够从console.log语句的输出中看到这一点。

有关更多信息,请参阅如何从异步回调函数返回值?我强烈建议研究一下这个答案,因为这种异步行为在处理 Web API 时非常普遍。

您的代码的修复是返回 a Promise,而不是尝试返回值:

GetToken(): Promise<string> {
  return new Promise((resolve, reject) => {
    this.afAuth.auth.onAuthStateChanged( user => {
      if (user) {
        user.getIdToken().then(idToken => {
          this.userToken = idToken;
          resolve(idToken);    
        });
      }
    });
  })
}
Run Code Online (Sandbox Code Playgroud)

简而言之:GetToken返回一个承诺,一旦 ID 令牌可用就解决。如果您在调用此函数时知道用户已经登录,则可以将其简化为:

GetToken(): string {
    const user = firebase.authentication().currentUser;
    return user.getIdToken()
}
Run Code Online (Sandbox Code Playgroud)

不同的是第二个函数不等待用户登录,所以如果没有登录用户就会失败。

然后,您可以像这样使用上述任何一个函数getEmployees

public getEmployees(): Observable<Employee[]> {
  return new Promise((resolve, reject) => 
    this.authService.GetToken().then((idToken) => {
      httpOptions = {
        headers: new HttpHeaders({
          'Content-Type': 'application/json',
          'Authorization': 'JWT ' + idToken
        }),
      };

      this.http.get<Employee[]>(`${this.url}/employee/`, this.httpOptions)
          .then(resolve).catch(reject);

    })
  })
}
Run Code Online (Sandbox Code Playgroud)