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

