跨选项卡调度访问令牌刷新

时间:2017-06-12 08:01:48

标签: javascript angular

要在我的Angular应用程序中对用户进行身份验证,我使用具有到期时间X秒的访问权限并刷新令牌,这些令牌可用于将身份验证延长另一个X秒。

所以流程就是这样:

  • 用户登录。访问和刷新令牌都存储在本地存储中
  • 设置了一个计时器(比X秒短5%)。
  • 当计时器完成时,刷新令牌请求将发送到服务器,并使用生成的(新)访问和刷新令牌更新本地存储。

我的问题是:

  • 如果我打开了多个标签,我将不可避免地在同时从多个标签触发刷新的情况下结束。服务器将接受第一个请求,但为后续请求抛出400 Bad Request - Invalid refresh token,因为它认为它们已被使用。

有谁知道如何解决这个问题?如何在选项卡/窗口之间同步事物?我有几个想法,但它们似乎都有点牵强:

  • 如果回复为400 Bad Request,请稍后重试(或检查是否已存在有效的更新令牌)。
  • 尝试通过在标签之间发布消息来跨选项卡同步服务器请求。

1 个答案:

答案 0 :(得分:2)

不要设置计时器,添加拦截器并捕获错误,如果出现 401 错误,请执行刷新令牌流程,然后使用新令牌重复失败的请求

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

return next.handle(request).pipe(
  catchError((error: HttpErrorResponse) => {
    if (error.status == 401) {
      return this.refreshToken(request, next);
     } 
    }
    return throwError(error);
  })
);


      private refreshingInProgress: boolean = false;
  private accessTokenSubject: BehaviorSubject<any> = new BehaviorSubject<any>(null);

  private refreshToken(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    if (!this.refreshingInProgress) {
      this.refreshingInProgress = true;
      this.accessTokenSubject.next(null);
      return this.authenticationService.refreshToken().pipe(
        switchMap((res: any) => {
          this.refreshingInProgress = false;
          this.accessTokenSubject.next(res);
          // repeat failed request with new token
          return next.handle(this.addToken(request, res));
        })
      );
    } else {
      // wait while getting new token
      return this.accessTokenSubject.pipe(
        filter((token) => token !== null),
        take(1),
        switchMap((token) => {
          // repeat failed request with new token
          return next.handle(this.addToken(request, token));
        })
      );
    }
  }
  
  private addToken(request: HttpRequest<any>, token: Credentials) {
    return request.clone({
      setHeaders: {
        Authorization: `Bearer ${token.access_token}`,
      },
    });
  }