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

Angular如何无需用户操作自动检测JWT过期并获取新JWT和刷新令牌

实现无操作自动刷新JWT的两种可行方案

方案1:定时主动检测刷新

在你的AuthService中增加定时任务逻辑,用户登录成功、token刷新成功后,根据JWT的过期时间设置定时任务,提前触发刷新:

  • 首先在AuthService中定义定时器变量:private refreshTokenTimer: any;
  • 修改setAuth方法,每次保存新token后计算定时时间:
public setAuth(authToken: AuthToken){
  this._storageService.saveToken(authToken.token?.toString());
  this._storageService.saveRefreshToken(authToken.refreshToken);
  // 清除之前的旧定时器
  if(this.refreshTokenTimer) clearTimeout(this.refreshTokenTimer);
  // 解析JWT过期时间
  const token = this._storageService.getToken();
  const expireTime = this._jwtHelperService.getTokenExpirationDate(token).getTime();
  const currentTime = Date.now();
  // 提前30秒刷新,可根据需求调整提前量
  const delay = expireTime - currentTime - 30 * 1000;
  if(delay > 0){
    this.refreshTokenTimer = setTimeout(async ()=>{
      await this.isUserAuthenticated();
    }, delay)
  }
}
  • 新增登出时清理定时器的逻辑,避免内存泄漏:
public logout(){
  if(this.refreshTokenTimer) clearTimeout(this.refreshTokenTimer);
  this._storageService.clearToken();
  this._storageService.clearRefreshToken();
  this._router.navigate([RouteConstant.SIGN_IN]);
}

这种方案可以实现即使用户无任何页面操作,也能在token快过期时自动刷新。

方案2:HTTP拦截器全局自动刷新(推荐和方案1结合使用)

新增Angular HTTP拦截器,所有后端请求返回401状态码时,自动触发token刷新逻辑,刷新成功后重发原本失败的请求,用户完全无感知:

  1. 新建auth-interceptor.ts:
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
  private isRefreshing = false;
  private refreshTokenSubject: BehaviorSubject<any> = new BehaviorSubject<any>(null);

  constructor(private _authService: AuthService, private _storageService: StorageService) {}

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    // 给请求加Authorization头
    const token = this._storageService.getToken();
    if (token) {
      request = request.clone({
        setHeaders: {
          Authorization: `Bearer ${token}`
        }
      });
    }

    return next.handle(request).pipe(
      catchError(error => {
        if (error.status === 401 && !request.url.includes('refresh')) {
          return this.handle401Error(request, next);
        }
        return throwError(() => error);
      })
    );
  }

  private handle401Error(request: HttpRequest<any>, next: HttpHandler) {
    if (!this.isRefreshing) {
      this.isRefreshing = true;
      this.refreshTokenSubject.next(null);

      return this._authService.refreshAuthToken({
        token: this._storageService.getToken(),
        refreshToken: this._storageService.getRefreshToken()
      }).pipe(
        switchMap((success: boolean) => {
          this.isRefreshing = false;
          if(success){
            this.refreshTokenSubject.next(this._storageService.getToken());
            // 重发原请求
            return next.handle(request.clone({
              setHeaders: {
                Authorization: `Bearer ${this._storageService.getToken()}`
              }
            }));
          }
          // 刷新失败跳登录
          this._authService.logout();
          return throwError(()=> 'Refresh token failed');
        }),
        catchError(err => {
          this.isRefreshing = false;
          this._authService.logout();
          return throwError(() => err);
        })
      );
    } else {
      return this.refreshTokenSubject.pipe(
        filter(token => token != null),
        take(1),
        switchMap(jwt => {
          return next.handle(request.clone({
            setHeaders: {
              Authorization: `Bearer ${jwt}`
            }
          }));
        })
      );
    }
  }
}
  1. 在app.module.ts的providers中注册拦截器:
providers: [
  { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }
]

额外优化建议

你现有代码中的refreshAuthToken方法判断逻辑有误,原代码if (newToken && newRefreshToken == null)的判断条件不符合预期,建议修改为:

if (!newToken || !newRefreshToken){
  return false
}
else {
  return true;
}

另外建议后端刷新接口增加刷新令牌的有效性校验、单次使用限制,避免刷新令牌被盗用的安全风险。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 01:36:05