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

查看:77
本文介绍了在角度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实际上是一个行为主体:tokenSubject: BehaviorSubject<string> = new BehaviorSubject<string>(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).

有了next(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

编辑2020年9月21日

修复链接

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

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