Angular http 拦截器,缺少 XSRF 令牌 - 保持或包装其他 htp 请求,直到 api-bootstrapapi 引导程序调用请求完成

问题描述

Angular 有时无法添加 XSRF 令牌,因此我们添加了拦截器进行双重检查并在丢失时添加令牌。然后我们发现它有时无法读取 cookie,所以我们添加了 3rd 方库来读取 cookie.. 但我们仍然面临生产错误,其中 XSRF 令牌在第一次尝试时丢失,尤其是当从另一个站点重定向到我们这边时,页面的后续刷新工作正常。

我们的一个理论是 cookie 尚未设置,即 cookie 的角度读取在实际设置 cookie 之前运行的竞争条件.. 认为遇到此错误的流量很低,我们需要解决这些错误并减少客户沮丧。

现在我们希望将请求保留几毫秒然后读取 cookie,即使没有找到 cookie,我们也希望执行第二次 /ws/bootstrap 调用。

我很难理解或想出这个拦截器中的代码。非常感谢任何建议或参考。

与此同时,我会尝试在评论中发布我修改过的代码,但到目前为止,这几乎是无效的代码。

import { HttpEvent,HttpHandler,HttpInterceptor,HttpRequest,HttpXsrfTokenExtractor } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { ILoggerInstance,LoggerService } from '@foo/logger-angular';
import { CookieService } from 'ngx-cookie-service';
import { Observable } from 'rxjs';

// Angular adds XSRF header only for Post,below code adds for all GET (i.e. any halHttp call)
// https://github.com/angular/angular/issues/20511
@Injectable()
export class HttpXsrfInterceptor implements HttpInterceptor {
  private readonly log: ILoggerInstance;
  private readonly xsrfHeaderName = 'X-XSRF-TOKEN';
  private readonly xsrfCookieName = 'XSRF-TOKEN';

  constructor(
    private readonly tokenExtractor: HttpXsrfTokenExtractor,private readonly cookieService: CookieService,private readonly logger: LoggerService
  ) {
    this.log = this.logger.getInstance('HttpXsrfInterceptor');
  }

  intercept(req: HttpRequest<any>,next: HttpHandler): Observable<HttpEvent<any>> {
    if (!req.headers.has(this.xsrfHeaderName)) {
      // We have seen header missing in prod for approx 10% traffic,so adding non angular way of retrieving cookie if angular failed for some reason.
      const token = this.tokenExtractor.getToken() || this.cookieService.get(this.xsrfCookieName);
      if (token) {
        req = req.clone({ headers: req.headers.set(this.xsrfHeaderName,token) });
      } else if (req.url !== 'ws/bootstrap' && req.url !== 'bootstrap') {
        // Exclude bootstrap it issues xsrf cookie
        this.log.error('Missing xsrf cookie');
      }
    }
    return next.handle(req);
  }
}

解决方法

我可以向您推荐一个简单的方法来对价值进行廉价的“轮询”:

intercept(req: HttpRequest<any>,next: HttpHandler): Observable<HttpEvent<any>> {
   const req$ = req.url === 'ws/bootstrap' || req.url === 'bootstrap'
      ? of(req)
      : this.addXsrfToken(req);

    return req$.pipe(switchMap(newReq => next.handle(newReq)));
  }

addXsrfToken(request: HttpRequest<any>) {
   return timer(0,100).pipe( // polling every 100msec
     map(() => this.tokenExtractor.getToken()),// try to read token
     first(token => !!token),// first token that is not null
     map(token => req.clone({ headers: req.headers.set(this.xsrfHeaderName,token) })) // will be converted to event
   );
}

如果你打算做出好的解决方案,这是非常可扩展的。您真的应该将 timer().(first) 替换为您的逻辑。其余的将保持不变。理想情况下,您希望异步处理您的令牌值。在这种情况下,值将是发送请求的事件

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...