【问题标题】:Angular 7: JWT expiration time check errorAngular 7:JWT 过期时间检查错误
【发布时间】:2020-12-15 14:44:52
【问题描述】:

我正在创建一个 Angular 应用程序。目前我想检查 JWT 过期时间。我想在每次 API 调用之前检查 JWT exp 时间。 例如:

  1. https://example.com/get-user-details(过期时间结束,然后调用刷新令牌API)
  2. https://example.com/refresh-token(获取一个新的有效 JWT 令牌,存储在本地存储中并完成第一次 API 调用 - https://example.com/get-user-details)

但我遇到了一个错误。

Uncaught (in promise): TypeError: You provide 'undefined' where a 流是预期的。您可以提供 Observable、Promise、Array 或 可迭代。

我认为它们是 async/await 函数的错误,但我无法解决此问题。

import {Inject, Injectable} from '@angular/core';
import {
    HttpClient,
    HttpErrorResponse,
    HttpEvent,
    HttpHandler,
    HttpInterceptor,
    HttpRequest,
    HttpResponse
} from '@angular/common/http';
import {Observable} from 'rxjs';
import {catchError, tap} from 'rxjs/operators';
import {SHttpConfig} from './http.conf';
import {StorageService} from '../../services/storage.service';
import {PopAlertService} from '../pop-alert/pop-alert.service';
import {Router} from '@angular/router';
import {JwtService} from '../../services/jwt.service';
import {getDecodedJWT} from '../../helpers/functions';

@Injectable()
export class SHttpInjectorService implements HttpInterceptor {
    cachedRequests: Array<HttpRequest<any>> = [];

    constructor(
        private storage: StorageService,
        private config: SHttpConfig,
        private pop: PopAlertService,
        private router: Router,
        private jwt: JwtService
    ) {
    }

    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        const user: any = this.storage.user;
        const addToken = !req.urlWithParams.includes('token');
        const token = user ? user.token : null;
        if (token && !req.url.includes('token=') && addToken) {
            this.modify(req, user, token).then(res => {
                req = res;
                return this.handleRequest(next, req, token);
            });
        } else {
            return this.handleRequest(next, req, token);
        }
    }

    private async modify(req, user, token) {
        let user_token = user.token;
        const expireDate = getDecodedJWT(token).exp;
        if (Date.now() >= expireDate * 1000) {
            const result = await this.jwt.getRefreshedJWT().toPromise();
            user_token = result.token;
            user.token = user_token;
            this.storage.user = user;
        }
        return req.clone({
            setHeaders: {Authorization: `Bearer ${user_token}`},
            setParams: {token: user_token}
        });
    }

    private handleRequest(next, req, token) {
        return next.handle(req).pipe(tap((event: HttpEvent<any>) => {
            if (event instanceof HttpResponse) {
                if (event.body.code === 401 && token) {
                    this.router.navigate(['/']).catch(err => {console.log(err); });
                    this.storage.removeData();
                    this.pop.open('Your Session has expired ');
                }
            }
        }, (err: any) => {
            if (err instanceof HttpErrorResponse) {
                if (err.status === 401) {
                    this.collectFailedRequest(req);
                } else if (err.status === 0 && err.message.match('Http failure response')) {
                    this.pop.open('Couldn\'t get response from server');
                }
            } else {
                // tslint:disable-next-line:no-shadowed-variable
                this.router.navigate(['/']).catch(err => {console.log(err); });
            }
        }));
    }

    public collectFailedRequest(request): void {
        this.cachedRequests.push(request);
    }
}

【问题讨论】:

    标签: angular jwt angular7


    【解决方案1】:

    正如错误所说的期望流而不是未定义,问题的原因在于 if 子句:

       if (token && !req.url.includes('token=') && addToken) {
            this.modify(req, user, token).then(res => { // <-- here
                 req = res;
                 return this.handleRequest(next, req, token);
            });
       }
    

    intercept 方法期望返回一个 Observable,当满足 if 条件时,它不会返回任何内容,因为您将 return 语句放在 async 中方法,因此编译器将回调函数放入回调队列并继续到下一行,因为没有任何内容返回 undefined。

    我建议使用 Observables 和 RXJS 运算符并执行以下操作:

    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        const user: any = this.storage.user;
        const addToken = !req.urlWithParams.includes('token');
        const token = user ? user.token : null;
        if (token && !req.url.includes('token=') && addToken) {
            return this.modify(req, next, user, token);
        } else {
            return this.handleRequest(next, req, token);
        }
    }
    
    private modify(req, next, user, token): Observable<HttpEvent<any>> {
        let user_token = user.token;
        const expireDate = getDecodedJWT(token).exp;
        if (Date.now() >= expireDate * 1000) {
          return this.jwt.getRefreshedJWT().pipe(flatMap((result) => {
            user_token = result.token;
            user.token = user_token;
            this.storage.user = user;
            const clonedReq =  req.clone({
                setHeaders: {Authorization: `Bearer ${user_token}`},
                setParams: {token: user_token}
            });
            return this.handleRequest(next, clonedReq, token);
          }));
        }   
        return this.handleRequest(next, req, token);
    }
    

    另外我认为如果你以不同的方式处理它会更好,因为使用这种方法你正在检查令牌是否在每个请求中都已过期,这会在所有对服务器的请求中增加一些额外的延迟,使得请求的持续时间超过了应有的时间,相反,您可以采用更懒惰的方法,如果令牌已过期,则从服务器返回错误,然后您可以存储请求,触发刷新令牌逻辑并最终恢复失败的请求。

    【讨论】:

      猜你喜欢
      • 2023-04-04
      • 2018-06-19
      • 2018-10-27
      • 2020-06-30
      • 1970-01-01
      • 2018-04-13
      • 2011-02-13
      • 2018-12-22
      • 2018-11-19
      相关资源
      最近更新 更多