首页 > 解决方案 > 刷新令牌角

问题描述

我创建了一个用于从我的 Angular 应用程序调用 API 的服务。在该服务中,我声明了 ROOT_URL 和 TOKEN 变量并为它们赋值。

在声明下方,使用上述 ROOT_URL 和 TOKEN 的 API 获取方法很少。

我面临的问题是,此 TOKEN 值每 24 小时过期一次,因此我必须每天更改该值。我使用以前的 TOKEN 通过邮递员获取刷新令牌。

有人可以给我一个解决方案,我该如何实现这将在每次 TOKEN 到期时自动发生?

标签: angularrefresh-token

解决方案


通常,来自 API 的 HTTP 响应标头包含一些内容,表明该客户端曾经通过身份验证,但现在有一个过期的令牌。通常,响应标头具有称为 token-expired 或 www-authenticate 的属性;您必须在开始刷新令牌过程之前检查这一点。

代码示例:

身份验证拦截器

import { Injectable } from '@angular/core';
import {
  HttpInterceptor,
  HttpRequest,
  HttpHandler,
  HttpEvent,
  HttpErrorResponse
} from '@angular/common/http';
import { AuthService } from '../services/auth.service';
import { Observable, BehaviorSubject, throwError } from 'rxjs';
import { environment } from 'src/environments/environment';
import { filter, switchMap, take, catchError } from 'rxjs/operators';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
  private tryingRefreshing = false;
  private refreshTokenSubject: BehaviorSubject<any> = new BehaviorSubject<any>(null);

  constructor(public authService: AuthService) { }

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const token = this.authService.getToken();
    request = this.addAuthorization(request, token);
    return next.handle(request).pipe(catchError(error => {
      if (error instanceof HttpErrorResponse && error.status === 401) {
        const tokenExpired = error.headers.get('token-expired');
        if (tokenExpired) {
          return this.handle401Error(request, next);
        }

        this.authService.logout();
        return throwError(error);
      } else {
        return throwError(error);
      }
    }));
  }

  private handle401Error(request: HttpRequest<any>, next: HttpHandler) {
    if (!this.tryingRefreshing) {
      this.tryingRefreshing = true;
      this.refreshTokenSubject.next(null);
      
     return this.authService.refreshToken().pipe(
        switchMap((token: any) => {
          this.tryingRefreshing = false;
          this.refreshTokenSubject.next(token);
          return next.handle(this.addAuthorization(request, token));
        }));

    } else {
      return this.refreshTokenSubject.pipe(
        filter(token => token != null),
        take(1),
        switchMap(jwt => {
          return next.handle(this.addAuthorization(request, jwt));
        }));
    }
  }

  addAuthorization(httpRequest: HttpRequest<any>, token: string) {
    return httpRequest = httpRequest.clone({
      setHeaders: {
        Authorization: `Bearer ${token}`
      }
    });
  }
}

刷新令牌

这只是展示 share() 方法的示例方法。

    refreshToken(): Observable<string> {
    return this.http.post<any>(`${this.baseUrl}/auth/token/refresh-token`, {}, { withCredentials: true })
      .pipe(
        share(),
        map((authResponse) => {
          this.currentAuthSubject.next(authResponse);
          this.addToLocalStorage(authResponse);
          return authResponse.token;
        }));
}
 

推荐阅读