Angular项目首次调用OidcSecurityService.getAccessToken()返回null问题
解决Angular OIDC拦截器首次请求Token为null的问题
问题分析
当前代码的核心问题在于:拦截器构造函数中订阅getAccessToken()是异步操作,但首次页面渲染时,第一个API请求会在token订阅回调触发前就执行,导致this.token仍为null。后续请求正常是因为此时token已经通过订阅回调被赋值,刷新页面后OIDC服务会从缓存中快速加载token,所以请求也能正常获取到。
解决方案
1. 拦截器内直接异步处理Token
不要在构造函数中缓存token,而是在intercept方法里直接订阅getAccessToken()的Observable,通过RxJS的switchMap操作符确保拿到token后再处理请求,避免异步时序问题。
修改后的拦截器代码:
import { Injectable } from "@angular/core"; import { Observable, switchMap } from "rxjs"; import { HttpInterceptor, HttpEvent, HttpRequest, HttpHandler, } from "@angular/common/http"; import { OidcSecurityService } from "angular-auth-oidc-client"; import { Router } from "@angular/router"; import { TokenValidatorService } from "../../services/token-validator.service"; export const UseOidcInterceptorHeader = "X-Oidc-Interceptor"; @Injectable() export class TokenInterceptor implements HttpInterceptor { constructor( private oidcSecurityService: OidcSecurityService, private tokenValidatorService: TokenValidatorService, private router: Router ) {} intercept( req: HttpRequest<unknown>, next: HttpHandler ): Observable<HttpEvent<unknown>> { // 跳过认证服务器的请求,避免循环拦截 if (req.url.includes("companyAuthServer")) { return next.handle(req); } // 订阅token流,确保拿到有效token后再处理请求 return this.oidcSecurityService.getAccessToken().pipe( switchMap((token) => { if (!token) { console.error( "OidcSecurityService, token not fetched: NO authorize header!", req.urlWithParams ); // 内部应用强制认证,直接跳转到登录页 this.router.navigate(["/oidc/login"]); return next.handle(req); } // 验证用户AD组权限 if (!this.tokenValidatorService.checkUserIsInAdGroup(token)) { this.router.navigate(["/oidc/unauthorized"]); return next.handle(req); } // 克隆请求并添加Bearer Token头 const requestForward = req.clone({ setHeaders: { Authorization: `Bearer ${token}` }, }); return next.handle(requestForward); }) ); } }
2. 应用初始化前完成OIDC认证检查
为了彻底避免首次请求时token未加载的问题,在应用初始化阶段调用OIDC服务的checkAuth()方法,确保用户完成认证后再加载应用内容。
在app.module.ts中配置APP_INITIALIZER:
import { APP_INITIALIZER, NgModule } from '@angular/core'; import { OidcSecurityService } from 'angular-auth-oidc-client'; // 初始化OIDC认证检查 export function initOidc(oidcSecurityService: OidcSecurityService) { return () => oidcSecurityService.checkAuth(); } @NgModule({ providers: [ { provide: APP_INITIALIZER, useFactory: initOidc, deps: [OidcSecurityService], multi: true } ] }) export class AppModule { }
关键说明
- 使用
switchMap将token流与请求流合并,保证每次请求都能获取到最新的token值,同时处理异步时序问题。 APP_INITIALIZER会在应用启动前执行OIDC认证检查,确保用户已登录且token已加载,从根源上解决首次请求无token的问题。- 保留了权限验证逻辑,确保不符合AD组要求的用户被导航到未授权页面。
内容的提问来源于stack exchange,提问作者Alf Kåre Lefdal
相关产品推荐
相关产品推荐

