【问题标题】:Angular 12 HttpInterceptor Wait until a http client method finishAngular 12 HttpInterceptor 等到http客户端方法完成
【发布时间】:2021-12-08 10:02:54
【问题描述】:

您好,我正在尝试检查令牌何时即将到期并使用访问令牌重新加载它。 主要问题是要重新加载它,我必须进行 http 调用以获取新令牌,然后将其重新保存在会话存储中。 主要问题是,因为 http 是异步的,它会重新加载令牌,但是因为我不知道如何等到此调用完成后再使用新令牌重试原始调用,所以它失败了。

这是代码:

身份验证服务: 这是被调用来更新令牌的服务

async reloadIdTokenFromAccess(accessToken : string | null) {
  console.log("Performing call to reload token");
  const resp = await this.httpClient.post<IdTokenDTO>(`${environment.backendUrl}/auth/tokenReload`,accessToken).toPromise();
  this.setIdToken(resp.idToken);
  this.setExpireAt(resp.expireAt);
  console.log("Data update in session storage")
}

拦截器

@Injectable()
export class HttpTokenInterceptor implements HttpInterceptor {

  constructor(private authServ : AuthService) {}

  intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {

    let token: string | null = this.authServ.getIdToken();
    let expiration: number | null = this.authServ.getExpireAt();
    let reloaded : boolean = false;

    var currentTime = new Date().getTime();

    if(expiration != null && !request.url.includes("tokenReload") && !request.url.includes("login")){
      if(expiration <= currentTime + 60000 && this.authServ.getAccessToken() != null){
        reloaded = true;
        console.log("Token expiration Time near to expire, reloading token")
        this.authServ.reloadIdTokenFromAccess(this.authServ.getAccessToken());
        token = this.authServ.getIdToken();
        console.log("Finish all full reloading")
      }
    }

    if(reloaded){
      console.log("Starting the request with the new token")
    }

    let originalReq = request;

    if(token){
      request = originalReq.clone({
        setHeaders: {
          authorization: `Bearer ${ token }`,
          'Content-Type': 'application/json'
        }
      });
    }

    return next.handle(request);
  }
}

如果我检查日志,我会看到以下内容:

您可以在重新加载令牌之前检查请求是否正在执行。 在继续该方法之前如何强制完成此请求?

类似于 java 中的 CompletableFuture 使用 get() 方法来阻止代码直到它完成

我也有这样的测试没有成功

async reloadIdTokenFromAccess(accessToken : string | null) {
  console.log("Performing call to reload token");
  await this.httpClient.post<IdTokenDTO>(`${environment.backendUrl}/auth/tokenReload`,accessToken).subscribe( (resp : IdTokenDTO) => {
    this.setIdToken(resp.idToken);
    this.setExpireAt(resp.expireAt);
    console.log("updates token in session storage")
  })
}
import { Injectable } from '@angular/core';
import {HttpRequest,HttpHandler,HttpEvent,HttpInterceptor} from '@angular/common/http';
import { from, Observable, Subscription } from 'rxjs';
import { AuthService } from '../services/auth.service';

@Injectable()
export class HttpTokenInterceptor implements HttpInterceptor {

  constructor(private authServ : AuthService) {}

  intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
    return from(this.handle(request, next));
  }

  async handle(request: HttpRequest<any>, next: HttpHandler) {

    const expiration: number | null = this.authServ.getExpireAt();
    const currentTime = new Date().getTime();
    let reloaded: boolean = false;

    if (expiration != null && !request.url.includes('tokenReload') &&!request.url.includes('login')) {
      if (expiration <= currentTime + 60000 && this.authServ.getAccessToken() != null) {
        reloaded = true;
        console.log('Token expiration Time near to expire, reloading token');
        await this.authServ.reloadIdTokenFromAccess(this.authServ.getAccessToken());
        console.log('Finish of call reaload token');
      }
    }

    const token = this.authServ.getIdToken();

    if (reloaded){ 
      console.log('Starting the request with the new token');
    }

    const originalReq = request;

    if (token) {
      request = originalReq.clone({
        setHeaders: {
          authorization: `Bearer ${token}`,
          'Content-Type': 'application/json',
        },
      });
    }
    return next.handle(request).toPromise();

  }
}

我很绝望。不知道该做什么(也许继续学习课程和教程,但我有 dedlines):(

提前致谢。

【问题讨论】:

  • 我猜你应该在拦截器中等待你的承诺?
  • this.authServ.reloadIdTokenFromAccess(this.authServ.getAccessToken()); 在线调用时,您永远不会等待承诺完成,您必须使用thenasync/await。老实说,虽然我会尽量保持这一切一致,并为堆栈中的所有内容使用 RxJs 或使用 Promise。我的偏好是 RxJs。
  • @Igor 你能解释得更好吗,我有点新手,实际上我的问题是我不知道如何让代码等到承诺完成
  • this.authServ.reloadIdTokenFromAccess 返回什么?它是可观察的还是承诺?

标签: angular async-await promise jwt interceptor


【解决方案1】:

我终于使用了一个计时器,每 X 分钟检查一次令牌。静默重装。 我把这个计时器放在应用程序的主要组件中,就是这样。

还是谢谢。


export class MainComponent implements OnInit, OnDestroy{
...
public subscription : Subscription;
  ngOnInit(): void {
    this.subscription = timer(0, (300 *1000)).pipe().subscribe(() => {
      //console.log("ejecutando recarga del token");
      const expiration: number | null = this.authServ.getExpireAt();
      const currentTime = new Date().getTime();

      let reloaded: boolean = false;

      if (expiration != null) {
        if (expiration <= currentTime + (350 * 1000) && this.authServ.getAccessToken() != null) {
          reloaded = true;
          //console.log('Token expiration Time near to expire, reloading token');
          this.authServ.reloadIdTokenFromAccess(this.authServ.getAccessToken());
        }
      }
    });
  }
  ngOnDestroy() {
    this.subscription.unsubscribe();
  }

【讨论】:

    【解决方案2】:

    您应该使用高阶可观察对象。这是一个很好的阅读 https://blogs.msmvps.com/deborahk/higher-order-observable/

    尝试执行以下操作:

    reloadIdTokenFromAccess(accessToken : string | null) : Observable<IdTokenDTO> {
        console.log("Performing call to reload token");
        return this.httpClient.post<IdTokenDTO>(`${environment.backendUrl}/auth/tokenReload`,accessToken)
          .tap( (resp : IdTokenDTO) => {
            this.setIdToken(resp.idToken);
            this.setExpireAt(resp.expireAt);
            console.log("updates token in session storage")
          });
      }
    
    @Injectable()
    export class HttpTokenInterceptor implements HttpInterceptor {
    
      constructor(private authServ : AuthService) {}
    
    intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
        const expiration: number | null = this.authServ.getExpireAt();
        const currentTime = new Date().getTime();
        let reloaded: boolean = false;
    
        if (expiration != null && !request.url.includes('tokenReload') &&!request.url.includes('login')) {
          if (expiration <= currentTime + 60000 && this.authServ.getAccessToken() != null) {
    
            console.log('Token expiration Time near to expire, reloading token');
    
            return this.reloadIdTokenFromAccess(this.authServ.getAccessToken()).pipe(
              concatMap(token => {
                request = request.clone({
                  setHeaders: {
                    authorization: `Bearer ${token}`,
                    'Content-Type': 'application/json',
                  }
                });
                return next.handle(request);
              })
            )
    
            console.log('Finish of call reaload token');
          }
        }
        
        return next.handle(request);
      }
    }
    
    
      
    

    concatMap 将等待前一个 HTTP Observable 完成,然后再将新值映射到 HTTP Observable

    【讨论】:

      【解决方案3】:

      在您的authService 中,使用Observables 返回http 响应

      reloadIdTokenFromAccess(accessToken : string | null): Observable<IdTokenDTO> {
          console.log("Performing call to reload token");
          return this.httpClient.post<IdTokenDTO>(`${environment.backendUrl}/auth/tokenReload`,accessToken);
      }
      

      而且,在你的拦截器中,你会这样处理:

      intercept(
          request: HttpRequest<unknown>,
          next: HttpHandler
        ): Observable<HttpEvent<unknown>> {
          const expiration: number | null = this.authServ.getExpireAt();
          const currentTime = new Date().getTime();
          let reloaded: boolean = false;
      
          if (
            expiration != null &&
            !request.url.includes('tokenReload') &&
            !request.url.includes('login')
          ) {
            if (
              expiration <= currentTime + 60000 &&
              this.authServ.getAccessToken() != null
            ) {
              reloaded = true;
              console.log('Token expiration Time near to expire, reloading token');
      
              // unwrap your response from the observable
              this.authServ
                .reloadIdTokenFromAccess(this.authServ.getAccessToken())
                .pipe(
                  tap((response: any) => {
                    this.authServ.setIdToken(response.idToken);
                    this.authServ.setExpireAt(response.expireAt);
                    console.log('Data update in session storage');
                  })
                )
                .subscribe(() => this.addToken(request, reloaded));
            }
          }
      
          const req = this.addToken(request, reloaded);
          return next.handle(req);
        }
      
        private addToken(request: HttpRequest<unknown>, reloaded: boolean) {
          const token = this.authServ.getIdToken();
          console.log('Finish all full reloading');
      
          if (reloaded) console.log('Starting the request with the new token');
      
          const originalReq = request;
      
          if (token) {
            request = originalReq.clone({
              setHeaders: {
                authorization: `Bearer ${token}`,
                'Content-Type': 'application/json',
              },
            });
          }
      
          return request;
        }
      

      【讨论】:

      • 是的,但是如果将所有内容都放在订阅中,我将省略为请求的其余部分添加承载令牌的部分,而无需重新加载令牌....我的意思是,这部分必须完成总是:让 originalReq = 请求; if (token) { request = originalReq.clone({ setHeaders: { authorization: Bearer ${token}, 'Content-Type': 'application/json', }, }); }
      • 我有更多请求通过这个拦截器来设置令牌。我已经更改了代码并测试了您的解决方案,但没有任何效果,因为当不需要重新加载令牌时,请求的其余部分会失败,因为拦截器没有接收到令牌
      • @GabrielGarcíaGarrido,我已经编辑了我的答案,如果它对你有用,请告诉我
      • 很抱歉,它不起作用,它会在更新令牌之前进行第一次调用,然后我得到一个未经授权的 :(。它不会等到 Observable 完成
      猜你喜欢
      • 2022-11-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-03-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多