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

如何在Angular调用.NET Core API前检查JWT token是否过期

通用解决方案:Angular HTTP 拦截器(最优方案)

Angular 官方提供的 HTTP 拦截器可以全局拦截所有通过 HttpClient 发起的请求与响应,不需要修改任何现有业务接口的调用逻辑,就能自动统一完成JWT过期校验、刷新、请求头挂载、异常处理等逻辑,是这类需求的标准实现。


1. 实现全局JWT鉴权拦截器

import { Injectable } from '@angular/core';
import {
  HttpRequest,
  HttpHandler,
  HttpEvent,
  HttpInterceptor,
  HttpErrorResponse
} from '@angular/common/http';
import { Observable, from, throwError } from 'rxjs';
import { catchError, switchMap } from 'rxjs/operators';
import { AuthService } from './auth.service';
import { Router } from '@angular/router';
import { RouteConstant } from './constants/route.constant';

@Injectable()
export class JwtInterceptor implements HttpInterceptor {
  constructor(
    private _authService: AuthService,
    private _router: Router
  ) {}

  intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
    // 过滤不需要鉴权的公共接口,比如登录、注册、验证码等
    const publicUrls = [RouteConstant.SIGN_IN, 'other/public/api'];
    if (publicUrls.some(url => request.url.includes(url))) {
      return next.handle(request);
    }

    // 异步处理token校验,转成Observable
    return from(this._authService.isUserAuthenticated()).pipe(
      switchMap(isAuth => {
        if (!isAuth) {
          this._router.navigate([RouteConstant.SIGN_IN]);
          return throwError(() => new Error('身份验证失败,请重新登录'));
        }

        // 自动给请求挂载Authorization头
        const token = this._storageService.getToken();
        const authReq = request.clone({
          headers: request.headers.set('Authorization', `Bearer ${token}`)
        });
        return next.handle(authReq);
      }),
      catchError((error: HttpErrorResponse) => {
        // 兜底处理:极端情况token在请求过程中过期,返回401直接跳登录
        if (error.status === 401) {
          this._authService.logout();
          this._router.navigate([RouteConstant.SIGN_IN]);
        }
        return throwError(() => error);
      })
    );
  }
}

2. 修复现有AuthService的逻辑bug

原有isUserAuthenticated方法中return await this.refreshAuthToken(authTokenClient);后的token校验代码为死代码,永远不会执行,修正后如下:

public async isUserAuthenticated(): Promise<boolean>{
  const token = this._storageService.getToken();
  if(token !== null){
    if(!this._jwtHelperService.isTokenExpired(token.toString())){
      return true;
    }
    else{
      if(!this._storageService.refreshTokenExists()){
        return false;
      }
      else{
        let authTokenClient: AuthTokenClient = {
          token: token as string,
          refreshToken: this._storageService.getRefreshToken() as string
        };
        const refreshSuccess = await this.refreshAuthToken(authTokenClient);
        // 刷新成功后再校验新token有效性
        return refreshSuccess && !this._jwtHelperService.isTokenExpired(this._storageService.getToken()?.toString());
      }
    }
  }
  else{
    return false;
  }
}

同时修正refreshAuthToken的判断逻辑,原有逻辑判断存在语法错误:

public async refreshAuthToken(authTokenClient: AuthTokenClient): Promise<boolean> {
  try {
    const response = await this._http.post<AuthToken>(environment.apiHost + UrlConstant.ACCOUNT_REFRESH, authTokenClient,{observe: 'response'}).toPromise();
    const newToken = response.body?.token;
    const newRefreshToken = response.body?.refreshToken;
    if (!newToken || !newRefreshToken) {
      return false;
    }
    this._storageService.saveToken(newToken);
    this._storageService.saveRefreshToken(newRefreshToken);
    return true;
  } catch (e) {
    // 刷新token失败(比如refresh token也过期),直接返回失败
    return false;
  }
}

3. 注册拦截器

在app.module.ts的providers数组中添加拦截器配置:

import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { JwtInterceptor } from './jwt.interceptor';

@NgModule({
  // ...其他配置
  providers: [
    {
      provide: HTTP_INTERCEPTORS,
      useClass: JwtInterceptor,
      multi: true
    }
  ]
})
export class AppModule { }

4. 改造后业务代码效果

原有业务代码中的手动鉴权判断可以完全删除,直接调用接口即可,所有鉴权逻辑都会被拦截器自动处理:

// app.component.ts 改造后代码
clickEvent(){
  this.api();
}

public api(){
  this._http.get("http://localhost:15363/WeatherForecast").subscribe(response => {
    console.log(response);
  }, err => {
    console.log(err)
  });
}

可选优化:避免并发请求重复刷新token

如果同一时间有多个请求触发token刷新,会导致多次调用刷新接口,可添加刷新锁和请求队列,确保同一时间只有一个刷新请求执行,所有等待请求在刷新成功后统一重发。


备选方案:封装通用HTTP服务

如果不使用拦截器,也可以对HttpClient进行二次封装,把get/post/put/delete等方法都封装一层,内置鉴权校验逻辑,业务代码直接调用封装后的通用HTTP服务即可,也能避免重复编写判断逻辑。


内容的提问来源于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.05 23:12:00