有两个关于refreshToken的问题

来源:7-1 Redux 的概念和实战(一)

123321_0015

2019-05-02

老师,您好!

第一个问题:我想问一些关于redux相关的问题。我发现当redux涉及到refreshToken时,会有些异步的问题,因为所有的返回值都是observable,且dispatch操作是没有顺序的,这样会导致不能先refreshToken后再去发送http请求。这里如果用effect应该怎么设计呢?谢谢老师!

第二个问题:我需要refreshToken时是在angular的拦截器里调的服务,这样就会有一个小问题,当在很短时间内有大量请求时,就会发生refreshToken的http请求服务还没有得到返回值时就再一次调用这个refreshToken请求服务,会造成refreshToken没有更新,以后会全部http请求失败,请问有什么好的解决方案吗?需要我每次请求时设置一个延时吗?谢谢老师!

这是在angular的拦截器中我写的代码

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

if (req.url.includes('authentication')) {
  return next.handle(req);
}

return this.authService.refresh(getJwtToken(), getRefreshToken()).pipe(
  take(1),
  switchMap(auth => {
    saveJwtToken(auth.token);
    saveRefreshToken(auth.refreshToken);

    //   req.headers.set('Authorization', `${auth.token_type} ${auth.token}`);
    let newHeaders = req.headers;
    newHeaders = newHeaders.delete('Authorization');
    newHeaders = newHeaders.append(
      'Authorization',
      `${auth.token_type} ${auth.token}`
    );

    const dummyrequest = req.clone({
      headers: newHeaders
    });
    return next.handle(dummyrequest);
  }),
  catchError(err => {
    this.router.navigate(['/login']);
    return of(err);
  })
);

}

写回答

2回答

接灰的电子产品

2019-05-03

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>> {

        // 首先检查 token 是否过期
        // 如果没有过期,在 Request Headere 添加鉴权头 addAuthenticationToken();

        // 如果过期
        if (tokenHasExpired()) {

            if (this.refreshTokenInProgress) {

                // 如果 refreshTokenInProgress 为真, 我们需要等到 refreshTokenSubject 有非 null 值
                // 此时才意味着新的 token 已经准备好了,我们可以重试请求
                return this.refreshTokenSubject.pipe(
                    filter(result => result !== null),
                    take(1),
                    switchMap(() => next.handle(this.addAuthenticationToken(request)))
                );

            } else {

                this.refreshTokenInProgress = true;

                // 设置 refreshTokenSubject 为 null,这样后继的请求会等待新 token 的生成
                this.refreshTokenSubject.next(null);

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

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

                                this.refreshTokenInProgress = false; // 设置 refreshTokenInProgress 为 False
                                this.refreshTokenSubject.next(token); // 发射新 token 

                                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) {
        // 从本地取 token
        const accessToken = this.accountService.getAccessToken();

        // 如果 access token 是 null,说明用户没有登录
        // 直接返回原来的请求
        if (!accessToken) {
            return request;
        }

        // clone 请求, 添加 header
        return request.clone({
            setHeaders: {
                Authorization: accessToken
            }
        });
    }

}


0
1
123321_0015
谢谢老师了!原来要用一个subject。期待老师的新课呀!
2019-05-03
共1条回复

接灰的电子产品

2019-05-02

refresh token 的操作可以放在interceptor 中,这样就不用每个请求都处理了。
此外如果先需要执行某个操作。然后再执行某个操作、这个和redux 无关。使用 rx 很方便的可以处理

first$.switchMap(f => second$)

0
1
123321_0015
老师,我修改了一下,添加了代码,能再看看吗?谢谢
2019-05-02
共1条回复

Angular打造企业级协作平台,让你在Angular领域中出类拔萃

全网首个介绍官方 Material 组件库用法与 Redux 在 Angular 中的应用

998 学习 · 536 问题

查看课程