web-dev-qa-db-ja.com

angular 7のインターセプターでリフレッシュトークンの後にhttpリクエストを繰り返し実行しようとしています

angular 7.のエラー401を受け取ったときに、更新トークンリクエストを自動化しようとしています。

その間に私はangular 7でそれを行う方法の多くのドキュメントを見つけません、そして私はangularまたはrxjsの以前の知識を持っていませんちょっとおかしい

私はそれはほぼ完了していると思いますが、何らかの理由で2番目の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;
  })
);
5
user10955671

以下は、更新トークンを呼び出すためのコードであり、更新トークンの呼び出しを取得した後、失敗したAPIを呼び出します。

ソースコード内のコメントは、フローを理解するのに役立ちます。以下のシナリオではテスト済みで問題ありません

1)401が原因で単一のリクエストが失敗した場合は、更新トークンが呼び出され、更新されたトークンで失敗したAPIが呼び出されます。

2)401が原因で複数のリクエストが失敗した場合、更新トークンが呼び出され、更新されたトークンで失敗したAPIが呼び出されます。

3)トークンAPIを繰り返し呼び出すことはありません

まだこのコードが機能しない新しいシナリオを誰かが見つけた場合は、私に知らせてください。テストし、それに応じて更新します。

import { Injectable } from "@angular/core";
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor, HttpErrorResponse, HttpHeaders, HttpClient, HttpResponse } from "@angular/common/http";

import { Observable } from "rxjs/Observable";
import { throwError, BehaviorSubject } from 'rxjs';
import { catchError, switchMap, tap, filter, take, finalize } from 'rxjs/operators';
import { TOKENAPIURL } from 'src/environments/environment';
import { SessionService } from '../services/session.service';
import { AuthService } from '../services/auth.service';

/**
 * @author Pravin P Patil
 * @version 1.0
 * @description Interceptor for handling requests which giving 401 unauthorized and will call for 
 * refresh token and if token received successfully it will call failed (401) api again for maintaining the application momentum
 */
@Injectable()
export class RefreshTokenInterceptor implements HttpInterceptor {

    private isRefreshing = false;
    private refreshTokenSubject: BehaviorSubject<any> = new BehaviorSubject<any>(null);


    constructor(private http: HttpClient, private sessionService: SessionService, private authService: AuthService) { }

    /**
     * 
     * @param request HttpRequest
     * @param next HttpHandler
     * @description intercept method which calls every time before sending requst to server
     */
    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        // Taking an access token
        const accessToken = sessionStorage.getItem('ACCESS_TOKEN');
        // cloing a request and adding Authorization header with token
        request = this.addToken(request, accessToken);
        // sending request to server and checking for error with status 401 unauthorized
        return next.handle(request).pipe(
            catchError(error => {
                if (error instanceof HttpErrorResponse && error.status === 401) {
                    // calling refresh token api and if got success extracting token from response and calling failed api due to 401                    
                    return this.handle401Error(request, next);
                } // If api not throwing 401 but gives an error throwing error
                else {
                    return throwError(error);
                }
            }));
    }

    /**
     * 
     * @param request HttpRequest<any>
     * @param token token to in Authorization header
     */
    private addToken(request: HttpRequest<any>, token: string) {
        return request.clone({
            setHeaders: { 'Authorization': `Bearer ${token}` }
        });
    }

    /**
     * This method will called when any api fails due to 401 and calsl for refresh token
     */
    private handle401Error(request: HttpRequest<any>, next: HttpHandler) {
        // If Refresh token api is not already in progress
        if (this.isRefreshing) {
            // If refreshTokenInProgress is true, we will wait until refreshTokenSubject has a non-null value
            // – which means the new token is ready and we can retry the request again
            return this.refreshTokenSubject
                .pipe(
                    filter(token => token != null),
                    take(1),
                    switchMap(jwt => {
                        return next.handle(this.addToken(request, jwt))
                    }));
        } else {
            // updating variable with api is in progress
            this.isRefreshing = true;
            // Set the refreshTokenSubject to null so that subsequent API calls will wait until the new token has been retrieved
            this.refreshTokenSubject.next(null);

            const refreshToken = sessionStorage.getItem('REFRESH_TOKEN');
            // Token String for Refresh token OWIN Authentication
            const tokenDataString = `refresh_token=${refreshToken}&grant_type=refresh_token`;
            const httpOptions = {
                headers: new HttpHeaders({
                    'Content-Type': 'application/x-www-form-urlencoded',
                    'X-Skip-Interceptor': ''
                })
            };
            return this.http.post<any>(TOKENAPIURL, tokenDataString, httpOptions)
                .pipe(switchMap((tokens) => {
                    this.isRefreshing = false;
                    this.refreshTokenSubject.next(tokens.access_token);
                    // updating value of expires in variable                    
                    sessionStorage.setItem('ACCESS_TOKEN', tokens.access_token);
                    return next.handle(this.addToken(request, tokens.access_token));
                }));
        }
    }
}
0
Pravin P Patil