Javascript 跨选项卡计划访问令牌刷新

Javascript 跨选项卡计划访问令牌刷新,javascript,angular,Javascript,Angular,为了对Angular应用程序中的用户进行身份验证,我使用到期时间为X秒的访问令牌和可用于将身份验证再延长X秒的刷新令牌 所以流程是这样的: 用户登录。访问和刷新令牌都存储在本地存储器中 设置计时器(比X秒短5%) 计时器完成后,将向服务器发送刷新令牌请求,并使用生成的(新)访问和刷新令牌更新本地存储 我的问题是: 如果我打开了多个选项卡,我将不可避免地在多个选项卡同时触发刷新的情况下结束。服务器将接受第一个请求,但对后续请求抛出400错误请求-无效刷新令牌,因为它认为这些请求已被使用 有

为了对Angular应用程序中的用户进行身份验证,我使用到期时间为X秒的访问令牌和可用于将身份验证再延长X秒的刷新令牌

所以流程是这样的:

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

  • 如果我打开了多个选项卡,我将不可避免地在多个选项卡同时触发刷新的情况下结束。服务器将接受第一个请求,但对后续请求抛出
    400错误请求-无效刷新令牌
    ,因为它认为这些请求已被使用
有人知道如何解决这个问题吗?如何跨选项卡/窗口同步内容?我有几个想法,但都有点牵强:

  • 如果响应是
    400错误请求
    ,请稍后重试(或检查是否已经有有效的更新令牌)
  • 通过在选项卡之间发布消息,尝试跨选项卡同步服务器请求

不要设置计时器,添加拦截器并捕获错误,如果出现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}`,
      },
    });
  }
intercept(请求:HttpRequest,下一步:HttpHandler):
可观察{
返回next.handle(request.pipe)(
catchError((错误:HttpErrorResponse)=>{
如果(error.status==401){
返回此.refreshToken(请求,下一步);
} 
}
返回投掷器(错误);
})
);
私有刷新进度:布尔值=false;
private accessTokenSubject:BehaviorSubject=new BehaviorSubject(null);
私有刷新令牌(请求:HttpRequest,下一步:HttpHandler):可观察{
如果(!this.refreshingProgress){
this.refreshingProgress=true;
this.accessTokenSubject.next(null);
返回此.authenticationService.refreshToken()管道(
开关映射((res:any)=>{
this.refreshingProgress=false;
this.accessTokenSubject.next(res);
//使用新令牌重复失败的请求
返回next.handle(this.addToken(request,res));
})
);
}否则{
//正在获取新令牌,请稍候
返回此.accessTokenSubject.pipe(
过滤器((令牌)=>令牌!==null),
以(1)为例,
开关映射((令牌)=>{
//使用新令牌重复失败的请求
返回next.handle(this.addToken(request,token));
})
);
}
}
私有addToken(请求:HttpRequest,令牌:凭据){
return request.clone({
集合标题:{
授权:`Bearer${token.access_token}`,
},
});
}

这可能会对您有所帮助