1

当我登录 API 向我发送tokentoken-life-time 时,当token-life-time即将结束时,我通过向 API 发送请求来刷新我的token并接收新的 token 和新的 refresh-token-time
当我刷新或导航到另一个页面时(在令牌生命周期结束的那一刻),我的拦截器发送令牌的旧值,当我再次刷新或导航到另一个页面时LocalStorage,API 给我一个错误“令牌不正确”发送正确的令牌。
但是如上所述,当令牌生命周期即将结束时,它会重复。
这是我的token-interceptor.service.ts

import { Injectable } from '@angular/core';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';
import { LoginService } from '../services/login.service';

@Injectable()
export class TokenInterceptorService implements HttpInterceptor {
  constructor(
    private loginService: LoginService
  ) { }
  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    if (
      this.loginService.isLogged
    ) {
      const token = localStorage.getItem('access-token');
      const headers = new HttpHeaders().set('Authorization', `Bearer ${token}`);
      request = request.clone({ headers: headers });
    }
    return next.handle(request);
  }
}

它需要令牌并向 API 发送请求。

我有login.service.ts登录和刷新功能。登录函数将令牌值放入LocalStorage并且如果var 为 true 并且它运行良好,则Refresh 函数会刷新令牌。LocalStorageisNeedToRefresh

refresh(): Observable<boolean> {
    return this.http.post(`${environment.auth}/refresh`, {
      token_hash: localStorage.getItem('refresh-token')
    }).pipe(
      map((res: any) => {
        if (res.access && res.refresh) {
          localStorage.setItem('access-token', res.access.hash);
          localStorage.setItem('expires-at-access', res.access.expires_at);
          localStorage.setItem('refresh-token', res.refresh.hash);
          localStorage.setItem('expires-at-refresh', res.refresh.expires_at);
          return true;
        } else {
          this.notificationService.error(res && res.result_descr || '');
          return false;
        }
      }),
      catchError(() => of(false))
    );
  }

这是我刷新令牌的地方login.component.ts

ngOnInit() {
    if (this.loginService.isLogged) {
      if (this.loginService.isNeedToRefresh === true) {
        this.loginService.refresh().subscribe((res: boolean) => {
          if (res === true) {
            this.router.navigate(['']);
          }
        });
      } else if (this.loginService.isNeedToRefresh === false) {
        this.router.navigate(['']);
      }
    }
  }

我也更新了我的令牌app.component.ts

ngOnInit() {
    $(document).on('click', '[href="#"]', e => e.preventDefault());

    this.router.events.subscribe((val) => {
      if (val instanceof NavigationEnd) {
        if (!(val.url.indexOf('/login') === 0)) {
          this.authWatcher();
        }
      }
    });
  }

authWatcher() {
    if (this.loginService.isLogged) {
      if (this.loginService.isNeedToRefresh === true) {
        this.loginService.refresh().subscribe((refresh: boolean) => {
          if (refresh === false) {
            this.authModalRef = this.modalService.show(this.staticModal, { backdrop: 'static' });
          } else {
            this.loginService.checkToken().subscribe((check: boolean) => {
              if (!check) {
                this.logoutService.logout();
                this.router.navigate(['login']);
              }
            });
          }
        });
  }
}

我的拦截器运行良好的最佳方式是什么?

一点点更新,这是我检查 isNeedToRefresh 的方法

 get isNeedToRefresh(): boolean {
    const accessExpireTimestamp = new Date(
      localStorage.getItem('expires-at-access')
    ).getTime();
    const refreshExpireTimestamp = new Date(
      localStorage.getItem('expires-at-refresh')
    ).getTime();
    const nowTimestamp = new Date().getTime();
    if (nowTimestamp >= accessExpireTimestamp) {
      if (nowTimestamp >= refreshExpireTimestamp) {
        return null; // Refresh token expired
      } else {
        return true; // Refresh token not expired
      }
    }
    return false;
  }
4

1 回答 1

1

这个决定对我有用,如果其他人会遇到这样的问题,我已经完全重写了我的拦截器,基于这个链接

import { Injectable } from '@angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http';
import { LoginService } from '../services/login.service';
import { Observable, BehaviorSubject, Subject } from 'rxjs';
import { switchMap, take, filter } from 'rxjs/operators';
@Injectable()
export class TokenInterceptorService implements HttpInterceptor {
  private refreshTokenInProgress = false;
  private refreshTokenSubject: Subject<any> = new BehaviorSubject<any>(null);

  constructor(public loginService: LoginService) { }

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    if (request.url.indexOf('refresh') !== -1) {
      return next.handle(request);
    }

    const accessExpired = this.loginService.accessExpired;
    const refreshExpired = this.loginService.refreshExpired;

    if (accessExpired && refreshExpired) {
      return next.handle(request);
    }
    if (accessExpired && !refreshExpired) {
      if (!this.refreshTokenInProgress) {
        this.refreshTokenInProgress = true;
        this.refreshTokenSubject.next(null);
        return this.loginService.refresh().pipe(
          switchMap((authResponse) => {
            console.log('authResponse ', authResponse)
            if (authResponse) {
              const token = localStorage.getItem('access-token');
              this.refreshTokenInProgress = false;
              this.refreshTokenSubject.next(token);
              return next.handle(this.injectToken(request));
            } else {
              return next.handle(request);
            }
          }),
        );
      } else {
        return this.refreshTokenSubject.pipe(
          filter(result => result !== null),
          take(1),
          switchMap((res) => {
            return next.handle(this.injectToken(request))
          })
        );
      }
    }

    if (!accessExpired) {
      return next.handle(this.injectToken(request));
    }
  }

  injectToken(request: HttpRequest<any>) {
    const token = localStorage.getItem('access-token');
    return request.clone({
      setHeaders: {
        Authorization: `Bearer ${token}`
      }
    });
  }
}
于 2020-10-13T06:23:33.710 回答