【问题标题】:Angular 6 HttpInterceptor - when getting 401 refresh the token and create the same requestAngular 6 HttpInterceptor - 获取 401 时刷新令牌并创建相同的请求
【发布时间】:2019-03-07 02:44:58
【问题描述】:

我在 Angular 6 中使用HttpInterceptor,并尝试构建一个刷新令牌机制:

当 httpClient 请求获得401 状态码(未经授权)时,HttpInterceptor 将创建一个刷新令牌的请求,它将更新第一个请求的标头并使用新令牌再次调用它。

代码一直有效,直到我需要使用从刷新令牌请求中获得的新令牌再次调用原始请求。 这是我的代码:

export class MyInterceptor implements HttpInterceptor {

constructor(public restService:RestService){}

intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
 return next.handle(request).pipe(
    tap(event => {
      if (event instanceof HttpResponse) {
        console.log('succeed');
      }
    }, error => {
        if(error.status==401){
            this.restService.refreshToken().subscribe(response => {
                this.restService.updateHeaders(response['token']);
                const newRequest = request.clone();
                return next.handle(newRequest);
              });
        }
    })
  )
}
}

【问题讨论】:

    标签: angular6 interceptor angular-httpclient angular-http-interceptors refresh-token


    【解决方案1】:

    您需要执行以下操作。您还需要确保将新标头附加到请求中。不知道你在哪里做,因为它不在这个拦截器中。附加它的最佳部分是在拦截器中。即使是这个,如果你真的在服务中这样做。

    // if multiple requests fail, do only one refresh request
    private readonly refreshToken$ = this.restService
        .refreshToken() //the refresh token method could update the token in it's internal state, not sure why the later call to updateHeaders
        .pipe(
            tap(response => this.restService.updateHeaders(response['token'])),
            ignoreElements(),
            shareReplay(1)
        );
    
    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        return next
            .handle(request)
            .pipe(
                catchError(error => {
                    if (error.status === 401) {
                        return concat(this.refreshToken$, throwError(new RetryRequestError()));
                    } else {
                        throw error;
                    }
                }),
                retryWhen(error => {
                    if (error instanceof RetryRequestError) {
                        // try the request again
                        return;
                    }
                    throw error;
                })
            );
    }
    
    class RetryRequestError extends Error {
        constructor() {
            super('retry_request');
            Object.setPrototypeOf(this, RetryRequestError.prototype);
        }
    }
    

    【讨论】:

    • Thnaks Andrei Tătar,
    【解决方案2】:

    我阅读了更多关于它的信息,并使用了某人编写的代码,我对其进行了一些修改和修改,最终它对我有用。 当然,如果有人使用这个代码,他应该修改它。

    import { Observable } from 'rxjs';
    import { HttpClient } from '@angular/common/http';
    import { catchError, switchMap } from 'rxjs/operators';
    import { Injectable } from "@angular/core";
    import { HttpInterceptor, HttpRequest, HttpHandler, HttpSentEvent, HttpHeaderResponse, HttpProgressEvent, HttpResponse, HttpUserEvent, HttpErrorResponse } from "@angular/common/http";
    import { _throw as observableThrowError } from 'rxjs/observable/throw';
    import { Router } from "@angular/router";
    import { environment } from '../../../environments/environment'
    
    @Injectable()
    export class RequestInterceptorService implements HttpInterceptor {
    
        public endPoints;
    
        constructor(public httpClient: HttpClient, public router: Router) { this.endPoints = environment.endPoints; }
    
        intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpSentEvent | HttpHeaderResponse | HttpProgressEvent | HttpResponse<any> | HttpUserEvent<any>> {
            return <any>next.handle(req.clone({ headers: req.headers.set('Cache-Control', 'no-cache').set('Pragma', 'no-cache'), withCredentials: true })).pipe(
                catchError(error => {
                    if (req.url.indexOf('refresh_token_login') != -1 && ((<HttpErrorResponse>error).status) == 401) {//the app created request for getting new token/session and got 401,meaning the refresh token/session also expired
                        this.router.navigate(['/logout']);
                    }
                    else if (((<HttpErrorResponse>error).status) == 401 && (<HttpErrorResponse>error).error.error_code == 401001) { // 401001 meaning that the token is invalid
                        this.router.navigate(['/logout']);
                    }
                    else if (((<HttpErrorResponse>error).status) == 401 && (<HttpErrorResponse>error).error.error_code == 401002) { // 401002 meaning that the token has expired
                        return this.handle401Error(req, next);
                    } else {
                        return observableThrowError(error.error);
                    }
                }));
        }
    
        handle401Error(req: HttpRequest<any>, next: HttpHandler) {
            return this.refreshToken().pipe(
                switchMap((res) => {
                    if (res.status == 200) {
                        return next.handle(this.getNewRequest(req));
                    }else{
                        return this.logoutUser();
                    }
                }),
                catchError(error => { 
                       return next.handle(this.getNewRequest(req));
                })
            )
        }
    
        getNewRequest(req: HttpRequest<any>): HttpRequest<any> {
            return req.clone({ headers: req.headers.set('Cache-Control', 'no-cache').set('Pragma', 'no-cache'), withCredentials: true });
        }
    
        logoutUser() {
            this.router.navigate(['/logout']);
            return observableThrowError("");
        }
    
        refreshToken() {
            return this.httpClient.get(this.endPoints.refreshToken, { observe: 'response' }).pipe(
                catchError(error => {
                    return observableThrowError(error);
                }));
        }
    }
    

    【讨论】:

    • 体面的解决方案!虽然这适用于并行请求,但它会在每个 401 上触发对 refresh_token 端点的调用;而不是调用 refresh_token 一次,然后重试所有失败的请求。
    猜你喜欢
    • 2018-11-07
    • 1970-01-01
    • 1970-01-01
    • 2022-08-15
    • 2012-02-16
    • 2019-04-16
    • 2017-10-09
    • 1970-01-01
    • 2023-03-15
    相关资源
    最近更新 更多