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
相关产品推荐
相关产品推荐

