angular - 尝试使用 Angular 7 中的拦截器在刷新 token 后重复 http 请求

标签 angular jwt angular-http-interceptors

我正在尝试在收到 Angular 为 7 的错误 401 时自动执行刷新 token 请求。

我没有找到太多关于如何使用 angular 7 的文档,而且我之前没有 angular 或 rxjs 的知识,我变得有点疯狂

我认为它几乎完成了,但由于某种原因,第二个 next.handle(newReq) 没有发送请求(在 google chrome 网络调试器中只出现第一个请求)

我正在获取刷新响应并正确生成 processLoginResponse(res)

你可以在这里看到我的拦截器

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

let newReq = req.clone();

return next.handle(req).pipe(
  catchError(error => {
    if (error.status == 401) {
      this._authenticationService.refresh().subscribe(
        res => {
          this._authenticationService.processLoginResponse(res);
          newReq.headers.set("Authorization", "Bearer " + this._authenticationService.authResponse.token)
          return next.handle(newReq)
        },
        error => {
          this._authenticationService.logOut();
        });
    }
    throw error;
  })
);

最佳答案

您必须区分所有请求。例如,您不想拦截您的登录请求,也不想拦截刷新 token 请求。 SwitchMap 是您最好的 friend ,因为您需要取消一些调用以等待 token 刷新。

所以您要做的是首先检查状态为 401(未授权)的错误响应:

return next.handle(this.addToken(req, this.userService.getAccessToken()))
            .pipe(catchError(err => {
                if (err instanceof HttpErrorResponse) {
                    // token is expired refresh and try again
                    if (err.status === 401) {
                        return this.handleUnauthorized(req, next);
                    }

                    // default error handler
                    return this.handleError(err);

                } else {
                    return observableThrowError(err);
                }
            }));

在您的 handleUnauthorized 函数中,您必须刷新 token 并同时跳过所有进一步的请求:

  handleUnauthorized (req: HttpRequest<any>, next: HttpHandler): Observable<any> {
        if (!this.isRefreshingToken) {
            this.isRefreshingToken = true;

            // Reset here so that the following requests wait until the token
            // comes back from the refreshToken call.
            this.tokenSubject.next(null);
            // get a new token via userService.refreshToken
            return this.userService.refreshToken()
                .pipe(switchMap((newToken: string) => {
                    // did we get a new token retry previous request
                    if (newToken) {
                        this.tokenSubject.next(newToken);
                        return next.handle(this.addToken(req, newToken));
                    }

                    // If we don't get a new token, we are in trouble so logout.
                    this.userService.doLogout();
                    return observableThrowError('');
                })
                    , catchError(error => {
                        // If there is an exception calling 'refreshToken', bad news so logout.
                        this.userService.doLogout();
                        return observableThrowError('');
                    })
                    , finalize(() => {
                        this.isRefreshingToken = false;
                    })
                );
        } else {
            return this.tokenSubject
                .pipe(
                    filter(token => token != null)
                    , take(1)
                    , switchMap(token => {
                        return next.handle(this.addToken(req, token));
                    })
                );
        }
    }

我们在拦截器类上有一个属性,用于检查是否已经有刷新 token 请求在运行:this.isRefreshingToken = true;因为当您触发多个未经授权的请求时,您不希望有多个刷新请求。

if (!this.isRefreshingToken) 中的所有内容部分是关于刷新您的 token 并再次尝试之前的请求。

else 中处理的一切适用于所有请求,与此同时,当您的 userService 刷新 token 时,将返回一个 tokenSubject,当 token 准备好时 this.tokenSubject.next(newToken);每个跳过的请求都将重试。

这篇文章是拦截器的起源灵感:https://www.intertech.com/angular-4-tutorial-handling-refresh-token-with-new-httpinterceptor/

编辑:

TokenSubject 实际上是一个行为主题:tokenSubject: BehaviorSubject<string> = new BehaviorSubject<string>(null); ,这意味着任何新订阅者都将获得流中的当前值,这将是我们上次调用 this.tokenSubject.next(newToken) 时的旧 token .

next(null)每个新订阅者都不会触发 switchMap部分,这就是为什么filter(token => token != null)是必要的。

this.tokenSubject.next(newToken) 之后使用新 token 再次调用每个订阅者都会触发 switchMap与新 token 分开。希望现在更清楚

编辑 21.09.2020

修复链接

关于angular - 尝试使用 Angular 7 中的拦截器在刷新 token 后重复 http 请求,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54327548/

相关文章:

javascript - 如何在 Angular 2 中将字符串的特定部分设为粗体

html - 如何从 Angular 2 组件中更改 CSS 样式

jwt - 如何在 Next.js 中实现身份验证

c# - 承载错误 - invalid_token - 找不到签名 key

Angular 拦截器在 Http 请求数据响应之前完成

angular - HttpInterceptor 不拦截 Angular 10 中的请求

angularjs - 在 Jasmine 测试中模拟 AngularJS $httpBackend 并使用装饰器时出现 "$httpBackend.when is not a function"错误

angular - 路由在 Angular 6 中无法正常工作

Angular : how to debounce an Observable?

jwt - 当提供 JWT 时,如何在 micronaut 后端加载 "user"