Angular 7 - 如何共享令牌更新 Observable? [英] Angular 7 - How can I share the token renewal Observable?

查看:19
本文介绍了Angular 7 - 如何共享令牌更新 Observable?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在每个请求之前,HTTP 拦截器会检查访问令牌是否已过期,如果是,它首先更新令牌,然后继续请求.

问题出在有多个请求的页面上.在这些页面上,拦截器会尝试为每个请求更新令牌.

在这种情况下,我如何共享令牌续订请求?

负责token更新的方法是这样的:

renewToken() {let refreshToken =//获取刷新令牌let accessToken =//获取访问令牌如果 (!refreshToken || !accessToken)返回;让请求数据 = {访问令牌:访问令牌,refreshToken: 刷新令牌}return this.http.post(`${this.baseUrl}/renewToken`, requestData).管道(//我在这里尝试过 share() shareReplay(1) 和 publishReplay(1) 但没有运气);}

这就是拦截器使用该方法的方式:

<预><代码>...//如果我们需要更新令牌返回 this.accountService.renewToken().管道(switchMap(t => {this.accountService.saveToken(t);token = this.accountService.getAccessToken();var newReq = this.setToken(req, to​​ken);返回 next.handle(newReq);}));...

解决方案

您需要检查刷新令牌请求是否正在进行,因为您不希望其他调用进来并再次调用 refreshToken.

这里我为你创建了 RefreshTokenInterceptor 类.

您只需要对其进行自定义并按照评论进行操作:

import { Injectable } from "@angular/core";从@angular/common/http"导入 { HttpInterceptor, HttpEvent, HttpRequest, HttpHandler };从rxjs"导入 { BehaviorSubject, Observable };import { catchError, switchMap, filter, take } from "rxjs/operators";@Injectable({提供在:'根'})导出类 RefreshTokenInterceptor 实现 HttpInterceptor {私人 refreshTokenInProgress: boolean = false;私有 refreshTokenSubject: BehaviorSubject= 新行为主体<任何>(空值);构造函数(公共帐户服务:帐户服务){}拦截(请求:HttpRequest,下一个:HttpHandler):Observable>{//首先检查令牌是否已过期//如果没有,只需添加 addAuthenticationToken();//如果过期如果(令牌已过期()){如果(this.refreshTokenInProgress){//如果 refreshTokenInProgress 为真,我们将等待直到 refreshTokenSubject 有一个非空值//– 这意味着新令牌已准备就绪,我们可以再次重试请求返回 this.refreshTokenSubject.pipe(过滤器(结果 => 结果 !== 空),采取(1),switchMap(() => next.handle(this.addAuthenticationToken(request))));} 别的 {this.refreshTokenInProgress = true;//将 refreshTokenSubject 设置为 null,以便后续的 API 调用将等到新令牌被检索到this.refreshTokenSubject.next(null);返回 this.accountService.renewToken().管道(switchMap(t => {this.accountService.saveToken(t);让令牌 = this.accountService.getAccessToken();this.refreshTokenInProgress = false;//设置 refreshTokenInProgress 为 Falsethis.refreshTokenSubject.next(token);//将令牌添加到 refreshTokenSubjectvar newReq = this.setToken(req, to​​ken);返回 next.handle(newReq);}),catchError((err) => {this.refreshTokenInProgress = false;返回 Observable.throw(err);}));}} 别的 {返回 this.addAuthenticationToken(request);}}addAuthenticationToken(请求){//从本地存储获取访问令牌const accessToken = this.accountService.getAccessToken();//如果访问令牌为空,则表示用户未登录//然后我们返回原始请求如果(!访问令牌){退货请求;}//我们克隆请求,因为原始请求是不可变的返回 request.clone({设置标题:{授权:accessToken}});}}

Before each request a HTTP interceptor checks if the access token is expired and if so, it first renews the token and then continues the request.

The problem is on pages that have multiple requests. On those pages the interceptor tries to renew the token for each request.

How can I share the token renewal request in this case?

The method that is responsible for token renewal is this:

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

And this is how the interceptor uses that method:

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

解决方案

You need to check if refresh token request is in progress or not because you don’t want other calls to come in and call refreshToken again.

Here I've created RefreshTokenInterceptor class for you.

You just need to customize it and follow the comments:

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

}

这篇关于Angular 7 - 如何共享令牌更新 Observable?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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