尝试在使用角度 7 中的拦截器刷新令牌后重复 http 请求 [英] Trying to repeat a http request after refresh token with a interceptor in angular 7

查看:13
本文介绍了尝试在使用角度 7 中的拦截器刷新令牌后重复 http 请求的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在收到带有角度 7 的错误 401 时自动执行刷新令牌请求.

I'm trying to automate the refresh token requests upon receiving an error 401 with angular 7.

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

Between that I do not find much documentation of how to do it with angular 7 and that I do not have previous knowledge of angular or rxjs I am becoming a little crazy

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

I think it's almost completed, but for some reason the second next.handle(newReq) dont send the request (in google chrome network debugger only apears first request)

我得到了刷新的响应并正确地制作了 processLoginResponse(res)

i'm gettin the response of refresh and making processLoginResponse(res) correctly

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

you can see here my interceptor

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;
  })
);

推荐答案

你必须区分所有的请求.例如,您不想拦截您的登录请求,也不想拦截刷新令牌请求.SwitchMap 是你最好的朋友,因为你需要取消一些调用来等待你的令牌被刷新.

You have to distingiush among all the requests. For example you don't want to intercept your login request and also not the refresh token request. SwitchMap is your best friend because you need to cancel some calls to wait for your token is getting refreshed.

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

So what you do is check first for error responses with status 401 (unauthorized):

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 函数中,您必须刷新您的令牌并同时跳过所有进一步的请求:

In your handleUnauthorized function you have to refresh your token and also skip all further requests in the meantime:

  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));
                    })
                );
        }
    }

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

We have an attribute on the interceptor class which checks if there is already a refresh token request running: this.isRefreshingToken = true; because you don't want to have multiple refresh request when you fire multiple unauthorized requests.

所以 if (!this.isRefreshingToken) 部分中的所有内容都是关于刷新您的令牌并再次尝试上一个请求.

So everthing within the if (!this.isRefreshingToken) part is about refreshing your token and try the previous request again.

else 中处理的所有内容都是针对所有请求的,与此同时,当您的 userService 刷新令牌时,会返回一个 tokenSubject,并且当令牌准备好 this.tokenSubject 时.next(newToken); 每个跳过的请求都会被重试.

Everything which is handled in else is for all requests, in the meantime while your userService is refreshing the token, a tokenSubject gets returned and when the token is ready with this.tokenSubject.next(newToken); every skipped request will be retried.

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

Here this article was the origin inspiration for the interceptor: https://www.intertech.com/angular-4-tutorial-handling-refresh-token-with-new-httpinterceptor/

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

TokenSubject is actually a Behavior Subject: tokenSubject: BehaviorSubject<string> = new BehaviorSubject<string>(null);, which means any new subscriber will get the current value in the stream, which would be the old token from last time we call this.tokenSubject.next(newToken).

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

Withnext(null) every new subscriber does not trigger the switchMap part, thats why filter(token => token != null) is neccessary.

this.tokenSubject.next(newToken) 再次使用新令牌调用之后,每个订阅者都会使用新令牌触发 switchMap 部分.希望现在更清楚了

After this.tokenSubject.next(newToken) is called again with a new token every subscriber triggers the switchMap part with the fresh token. Hope it is more clearly now

编辑 21.09.2020

修复链接

这篇关于尝试在使用角度 7 中的拦截器刷新令牌后重复 http 请求的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆