【问题标题】:Refresh token Angular刷新令牌角
【发布时间】:2020-03-18 17:07:27
【问题描述】:

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

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

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

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

【问题讨论】:

  • 您可以创建一个拦截器并在那里检查错误请求是否是由于无效令牌引起的,然后请求新令牌并重做原始请求

标签: angular refresh-token


【解决方案1】:

您可以使用 HTTP 拦截器。 你可以在 Angular Academy 上查看article

您可以在下面找到我根据需要实现的示例(我已使用该文章作为此实现的起点)。此示例假定您正在后端生成刷新令牌。在我的后端,我正在生成一个访问令牌(有效期较短)和一个刷新令牌(有效期较长)。我仅将刷新令牌用于生成新的访问令牌而不用于授权。 例如,您可以将令牌存储在本地存储或 cookie 中,然后在服务中从那里检索它们。

import { Injectable } from '@angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor, HttpErrorResponse } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { switchMap, catchError } from 'rxjs/operators';
import { AuthService } from './auth.service';
import { IUserResponse } from '../shared/user.model';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
  // for avoiding entering an infinite loop
  private isRefreshing = false;

  constructor(private authService: AuthService) {}

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    if (this.authService.accessToken) {
      request = this.setToken(request, this.authService.accessToken);
    }

    return next.handle(request).pipe(
      catchError(error => {
        if (error instanceof HttpErrorResponse && error.status === 401 && this.authService.refreshToken) {
          return this.handleAuthorizationError(request, next);
        } else {
          return throwError(error);
        }
      })
    );
  }

  private setToken(request: HttpRequest<any>, token: string): HttpRequest<any> {
    return request.clone({ setHeaders: { Authorization: `Bearer ${token}` } });
  }

  private handleAuthorizationError(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    if (!this.isRefreshing) {
      this.isRefreshing = true;

     // I have created a route on my back-end to generate a new access token
      return this.authService.getRefreshToken().pipe(
        switchMap((response: IUserResponse) => {
          this.isRefreshing = false;

          return next.handle(this.setToken(request, response.user.accessToken));
        })
      );
    } else {
      return next.handle(request);
    }
  }
}

【讨论】:

    【解决方案2】:

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

    代码示例:

    AuthInterceptor

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

    【讨论】:

      猜你喜欢
      • 2020-02-17
      • 2019-07-09
      • 2021-08-20
      • 2017-07-01
      • 2022-10-31
      • 2016-01-05
      • 2019-06-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多