Angular 7 - Как я могу поделиться обновлением токена Observable? - PullRequest
0 голосов
/ 11 января 2019

Перед каждым запросом HTTP-перехватчик проверяет, не истек ли токен доступа, и если да, то сначала обновляет токен, а затем продолжает запрос.

Проблема на страницах с несколькими запросами. На этих страницах перехватчик пытается обновить токен для каждого запроса.

Как я могу поделиться запросом на продление токена в этом случае?

Метод, который отвечает за обновление токена, таков:

renewToken() {

    let refreshToken = // get refresh token

    let accessToken = // get access token

    if (!refreshToken || !accessToken)
      return;

    let requestData = {
      accessToken: accessToken,
      refreshToken: refreshToken
    }

    return this.http.post<ApplicationToken>(`${this.baseUrl}/renewToken`, requestData)
      .pipe(
        // I tried share() shareReplay(1) and publishReplay(1) here but no luck
      );
}

И вот как перехватчик использует этот метод:

...
// in case we need to renew the token
return this.accountService.renewToken()
  .pipe(                        
    switchMap(t => {

      this.accountService.saveToken(t);
      token = this.accountService.getAccessToken();

      var newReq = this.setToken(req, token);
      return next.handle(newReq);
    })
  );
...

1 Ответ

0 голосов
/ 11 января 2019

Вам необходимо проверить, выполняется ли запрос на обновление токена или нет, потому что вы не хотите, чтобы поступали другие вызовы, и снова вызывать refreshToken.

Здесь я создал класс RefreshTokenInterceptor для вас.

Вам просто нужно настроить его и следовать комментариям:

import { Injectable } from "@angular/core";
import { HttpInterceptor, HttpEvent, HttpRequest, HttpHandler } from "@angular/common/http";
import { BehaviorSubject, Observable } from "rxjs";
import { catchError, switchMap, filter, take } from "rxjs/operators";

@Injectable({
    providedIn: 'root'
})
export class RefreshTokenInterceptor implements HttpInterceptor {

    private refreshTokenInProgress: boolean = false;

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

    constructor(public accountService: AccountService) {}

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

        // Check first if token has expired
        // If not, just add addAuthenticationToken();

        // If expired
        if (tokenHasExpired()) {

            if (this.refreshTokenInProgress) {

                // 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(result => result !== null),
                    take(1),
                    switchMap(() => next.handle(this.addAuthenticationToken(request)))
                );

            } else {

                this.refreshTokenInProgress = true;

                // Set the refreshTokenSubject to null so that subsequent API calls will wait until the new token has been retrieved
                this.refreshTokenSubject.next(null);

                return this.accountService.renewToken()
                        .pipe(                        
                            switchMap(t => {

                                this.accountService.saveToken(t);
                                let token = this.accountService.getAccessToken();

                                this.refreshTokenInProgress = false; // Set refreshTokenInProgress to False
                                this.refreshTokenSubject.next(token); // Add token to the refreshTokenSubject

                                var newReq = this.setToken(req, token);
                                return next.handle(newReq);

                            }),
                            catchError((err) => {

                                this.refreshTokenInProgress = false;
                                return Observable.throw(err);

                            })
                        );
            }

        } else {

            return this.addAuthenticationToken(request);

        }

    }

    addAuthenticationToken(request) {
        // Get access token from Local Storage
        const accessToken = this.accountService.getAccessToken();

        // If access token is null this means that user is not logged in
        // And we return the original request
        if (!accessToken) {
            return request;
        }

        // We clone the request, because the original request is immutable
        return request.clone({
            setHeaders: {
                Authorization: accessToken
            }
        });
    }

}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...