You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular 5中收到403时如何用AWS Cognito的Refresh Token刷新令牌?

在Angular中实现Cognito令牌自动刷新(客户端处理)

刚好我之前做过类似的需求,完全可以在Angular客户端搞定,不用依赖.NET后端。下面是一步步的实现方案:

1. 安装依赖

首先安装AWS官方的Cognito JavaScript SDK,这是客户端处理Cognito操作的核心工具:

npm install amazon-cognito-identity-js --save

2. 创建令牌管理服务(TokenService)

这个服务专门负责令牌的存储、刷新逻辑,以及维护刷新状态,避免重复请求:

import { Injectable } from '@angular/core';
import { CognitoUserPool, CognitoUser } from 'amazon-cognito-identity-js';
import { BehaviorSubject, Observable } from 'rxjs';

// 替换成你的Cognito配置信息
const COGNITO_CONFIG = {
  UserPoolId: '你的UserPoolID',
  ClientId: '你的AppClientID'
};

@Injectable({ providedIn: 'root' })
export class TokenService {
  private userPool: CognitoUserPool;
  private currentUser: CognitoUser | null = null;
  private refreshInProgress$ = new BehaviorSubject<boolean>(false);

  constructor() {
    this.userPool = new CognitoUserPool(COGNITO_CONFIG);
    this.currentUser = this.userPool.getCurrentUser();
  }

  // 获取本地存储的RefreshToken
  getRefreshToken(): string | null {
    if (!this.currentUser) return null;
    let refreshToken = null;
    this.currentUser.getSession((err, session) => {
      if (!err && session.isValid()) {
        refreshToken = session.getRefreshToken().getToken();
      }
    });
    return refreshToken;
  }

  // 核心:刷新令牌并更新本地存储
  refreshTokens(): Observable<{ idToken: string, accessToken: string }> {
    return new Observable((observer) => {
      if (!this.currentUser) {
        observer.error('未找到已认证用户');
        return;
      }

      this.refreshInProgress$.next(true);
      this.currentUser.refreshSession(this.currentUser.getSession().getRefreshToken(), (err, newSession) => {
        this.refreshInProgress$.next(false);
        if (err) {
          observer.error(err);
          return;
        }

        // 更新本地存储的令牌(这里用localStorage,也可以换成sessionStorage)
        const newIdToken = newSession.getIdToken().getJwtToken();
        const newAccessToken = newSession.getAccessToken().getJwtToken();
        localStorage.setItem('id_token', newIdToken);
        localStorage.setItem('access_token', newAccessToken);

        observer.next({ idToken: newIdToken, accessToken: newAccessToken });
        observer.complete();
      });
    });
  }

  // 检查是否正在刷新令牌,防止重复请求
  isRefreshing(): boolean {
    return this.refreshInProgress$.value;
  }
}

3. 创建HTTP拦截器,自动捕获403并刷新令牌

Angular的HttpInterceptor可以全局拦截所有HTTP请求和响应,这是实现自动刷新的最佳位置:

import { Injectable } from '@angular/core';
import {
  HttpRequest,
  HttpHandler,
  HttpEvent,
  HttpInterceptor,
  HttpErrorResponse
} from '@angular/common/http';
import { Observable, catchError, switchMap } from 'rxjs';
import { TokenService } from './token.service';
import { Router } from '@angular/router';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {

  constructor(private tokenService: TokenService, private router: Router) {}

  intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
    // 给请求添加Authorization头(使用当前的AccessToken)
    const accessToken = localStorage.getItem('access_token');
    if (accessToken) {
      request = request.clone({
        setHeaders: {
          Authorization: `Bearer ${accessToken}`
        }
      });
    }

    // 处理响应错误
    return next.handle(request).pipe(
      catchError((error: HttpErrorResponse) => {
        // 只处理令牌过期导致的403错误
        if (error.status === 403 && !this.tokenService.isRefreshing()) {
          // 调用刷新令牌方法,成功后重发原请求
          return this.tokenService.refreshTokens().pipe(
            switchMap((newTokens) => {
              const refreshedRequest = request.clone({
                setHeaders: {
                  Authorization: `Bearer ${newTokens.accessToken}`
                }
              });
              return next.handle(refreshedRequest);
            }),
            catchError((refreshErr) => {
              // 刷新失败(比如RefreshToken也过期了),清除令牌并跳转登录页
              localStorage.removeItem('id_token');
              localStorage.removeItem('access_token');
              this.router.navigate(['/login']);
              throw refreshErr;
            })
          );
        }

        // 其他错误直接抛出,交给业务组件处理
        throw error;
      })
    );
  }
}

4. 注册拦截器

最后在app.module.ts中把拦截器注册到全局:

import { NgModule } from '@angular/core';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { AuthInterceptor } from './auth.interceptor';

@NgModule({
  imports: [HttpClientModule],
  providers: [
    {
      provide: HTTP_INTERCEPTORS,
      useClass: AuthInterceptor,
      multi: true
    }
  ]
})
export class AppModule { }

关键注意事项

  • 避免重复刷新:用refreshInProgress$标记刷新状态,防止多个请求同时触发刷新操作。
  • 令牌存储选择:示例用了localStorage,如果你需要更安全的存储,可以换成sessionStorage或者Angular的@ngx-pwa/local-storage包。
  • 异常边界处理:如果RefreshToken也过期,刷新会失败,此时必须清除本地令牌并强制跳转登录页。
  • 用户状态校验:确保currentUser是有效的,用户登录后Cognito会自动在本地存储用户会话,getCurrentUser()可以直接获取。

这样配置后,当API返回403时,Angular会自动用RefreshToken刷新令牌,然后静默重发原请求,用户完全感知不到这个过程,只有当RefreshToken也过期时才会跳转到登录页~

内容的提问来源于stack exchange,提问作者xaisoft

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 08:22:42