【问题标题】:Refreshing token for parallel HTTP requests using HttpInterceptor使用 HttpInterceptor 刷新并行 HTTP 请求的令牌
【发布时间】:2018-06-21 18:49:55
【问题描述】:

我正在开发一个 Ionic 应用程序,并尝试在用户收到 HTTP 请求的 401 响应时兑现刷新令牌。我在网上找到了一些示例,并且能够让这个 (https://www.intertech.com/Blog/angular-4-tutorial-handling-refresh-token-with-new-httpinterceptor/) 工作,但同时出现多个请求。

我遇到的问题是这一系列调用中的第一个调用调用刷新令牌并成功重试,而其他调用从未重试。如果我为已经在进行刷新的请求使用 .filter 和 .take off 主题返回,则调用确实会重试,但没有新令牌。在可观察对象和主题方面,我还很陌生,所以我不太确定问题可能是什么。

请求

  this.myService.getData().subscribe(response => {this.data = response.data;}); 
  this.myService.getMoreData().subscribe(response => {this.moreData = response.data;}); 
  this.myService.getEvenMoreData().subscribe(response => {this.evenMoreData = response.data;}); 

拦截器

@Injectable()
export class HttpInterceptor implements HttpInterceptor {

  isRefreshingToken: boolean = false;
  tokenSubject = new BehaviorSubject<string>(null);   

  tokenService: tokenService;

  constructor(private authService: AuthService, private injector: Injector) { }

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

    return this.authService.getUser().flatMap(user => {
      request = this.addToken(request, next, user.accessToken);

      return next
        .handle(request)
        .catch(error => {
          if (error instanceof HttpErrorResponse) {
            switch ((<HttpErrorResponse>error).status) {
              case 401:
                return this.handle401(request, next, user);
            }
          } else {
          return Observable.throw(error);
        };
      })
    });


  }

  addToken(request: HttpRequest<any>, next: HttpHandler, accessToken: string): HttpRequest<any> {
    return request.clone({ setHeaders: { Authorization: 'Bearer ' + accessToken }})
  }

  handle401(request: HttpRequest<any>, next: HttpHandler, user: any) {

    if (!this.isRefreshingToken) {
      this.isRefreshingToken = true;
      this.tokenSubject.next(null);
      this.tokenService = this.injector.get(tokenService);
      return this.tokenService.refresh(user.refreshToken)
        .switchMap(refreshResponse => {
          if (refreshResponse) {
            this.authService.setUser(refreshResponse.id_token, refreshResponse.access_token, refreshResponse.refresh_token);
            this.tokenSubject.next(refreshResponse.accessToken);
            return next.handle(this.addToken(request, next, refreshResponse.access_token));
          }
          else {
             //no token came back. probably should just log user out.
          }
        })
        .finally(() => {
          this.isRefreshingToken = false;
      });      
    }
    else {
      return this.tokenSubject
        .filter(token => token != null)
        .take(1)
        .switchMap(token => {
          return next.handle(this.addToken(request, next, token));
        });
    }

  }



}

【问题讨论】:

    标签: javascript angular rxjs


    【解决方案1】:

    在我看来你没有正确的令牌:

    • 你有:

      this.tokenSubject.next(refreshResponse.accessToken);

    • 应该是:

      this.tokenSubject.next(refreshResponse.access_token);

    【讨论】:

      【解决方案2】:

      我实际上最终通过将主题移动到我的身份验证服务并在 setUser 方法中执行下一步来解决这个问题。然后在我的 401 方法的 else 语句中,我从我的身份验证服务上的一个新方法返回了主题并修复了它。我仍然需要 take(1) 但能够摆脱过滤器,因为我最终没有使用 BehaviorSubject。

      【讨论】:

        【解决方案3】:

        我过去也遇到过类似的问题。由于某种未知原因(至少对我而言),当我拦截 401 时,我进行了刷新并重试,但重试操作被取消了。

        尽管如此,我意识到我可以在客户端读取 JWT 过期时间,所以我通过保存令牌过期时间来欺骗系统。然后我让路由事件(比如onViewWillEnter)检查过期时间,如果令牌过期,则刷新它。

        这种机制对用户是完全透明的,如果用户在没有执行 HTTP 请求的情况下停留太久,则确保身份验证令牌和刷新令牌过期,最重要的是,减少延迟,因为您永远不会收到 401 响应(在您的场景中,转换为三个请求)。

        实现这一点的一个简单方法是通过警卫:

        canActivate(route: ActivatedRouteSnapshot,
          state: RouterStateSnapshot) {
            if (this.refreshTokenService.isExpired) {
                this.tokenEvent_.next();
                return false;
            } else {
                this.refreshTokenService.refresh();
            }
        

        其中refreshTokenService 是一个实用服务,它具有令牌和通过HTTP 执行刷新的方法。 tokenEvent 是一个rxjs/Subject:它在保护构造函数中被订阅,每次有新事件到来时,它都会重定向到登录页面。

        在每条路由上添加此保护可确保令牌始终未过期。

        【讨论】:

        • 你有一些源代码可以显示吗?比如路由事件部分...
        猜你喜欢
        • 2018-11-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-01-05
        • 2017-04-21
        • 2017-11-07
        • 1970-01-01
        • 2023-03-15
        相关资源
        最近更新 更多