Angular中signinRedirect未定义及依赖注入错误排查
Angular认证服务两类错误的解决方案
错误说明
1. 初始依赖注入错误
Uncaught (in promise): Error: StaticInjectorError[PublicKeyService]
原因:PublicKeyService未在Angular模块的providers数组中注册,导致依赖注入机制无法找到该服务实例。
2. 路由守卫触发的空指针错误
Uncaught (in promise): TypeError: Cannot read properties of undefined (reading 'signinRedirect')
原因:AuthenticationSecureAuthService中的manager(UserManager实例)通过异步HTTP请求初始化,路由守卫AuthGuard调用startAuthentication()时,异步请求尚未完成,manager仍为undefined。
解决方案
1. 修复依赖注入错误
在你的Angular模块(如AppModule或对应功能模块)的providers数组中添加相关服务:
import { NgModule } from '@angular/core'; import { PublicKeyService } from './security/services/app.public-key-service'; import { AuthenticationSecureAuthService } from './security/services/authentication-secure-auth-service'; import { AuthGuard } from './security/services/auth.guard'; @NgModule({ // ...其他模块配置 providers: [ PublicKeyService, AuthenticationSecureAuthService, AuthGuard // ...其他需要注册的服务 ] }) export class AppModule { }
2. 修复异步初始化导致的空指针错误
修改AuthenticationSecureAuthService,用BehaviorSubject跟踪manager的初始化状态,确保认证操作仅在实例就绪后执行:
修改后的AuthenticationSecureAuthService关键代码:
import {Injectable, OnDestroy} from '@angular/core'; // ...保留原导入 import {BehaviorSubject} from 'rxjs'; import {first} from 'rxjs/operators'; @Injectable() export class AuthenticationSecureAuthService implements OnDestroy { private readonly loginReturnUrlKey: string = 'loginReturnUrlKey'; private user: User = null; private manager: UserManager; // 用于跟踪manager是否初始化完成 private managerReady$ = new BehaviorSubject<boolean>(false); constructor( private http: HttpClient, private userService: UserService, private router: Router, private publicKeyService: PublicKeyService ) { Oidc.Log.logger = console; Oidc.Log.level = Oidc.Log.INFO; this.publicKeyService.getClientSettings().subscribe( data => { console.log(data); this.manager = new UserManager(data); this.start(); // 标记manager初始化完成 this.managerReady$.next(true); } ); } // ...保留原其他方法 startAuthentication(): Promise<void> { // 等待manager就绪后再执行跳转 return this.managerReady$.pipe( first(isReady => isReady) ).toPromise().then(() => { return this.manager.signinRedirect(); }); } // ...保留原其他方法 }
修改AuthGuard的checkLoggedIn方法
由于startAuthentication()现在返回Promise,checkLoggedIn需要返回Promise<boolean>,让路由守卫等待异步操作完成:
private checkLoggedIn(url: string): Promise<boolean> { const loggedIn = this.userService.isLoggedIn(); const sessionCurrent = this.userService.isSessionCurrent(); if (loggedIn && sessionCurrent) { return Promise.resolve(true); } if (loggedIn && !sessionCurrent) { this.userService.clearCurrentUserAndToken(); this.alertService.error('Your session has expired.', true); } if (url && url.charAt(0) !== '/') { url = '/' + url; } this.authService.setLoginReturnUrl(url); // 执行认证跳转后返回false return this.authService.startAuthentication().then(() => false); }
同步更新canActivate和canLoad的返回类型:
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> { return this.checkLoggedIn(state.url); } canLoad(route: Route): Promise<boolean> { console.log(route.path); return this.checkLoggedIn(route.path); }
额外修复:PublicKeyService中的this指向问题
getClientSettings方法内的add_signing_keys函数使用普通函数导致this指向丢失,改成箭头函数保留实例上下文:
public getClientSettings() { const metadata = { issuer: `https://login.microsoftonline.com/${this.tenantId}/v2.0`, authorization_endpoint: `https://login.microsoftonline.com/${this.tenantId}/oauth2/v2.0/authorize`, token_endpoint: `https://login.microsoftonline.com/${this.tenantId}/oauth2/v2.0/token` }; // 改成箭头函数,确保this指向PublicKeyService实例 const add_signing_keys = (keys_array) => { return { metadata: metadata, singingKeys: keys_array, authority: `https://login.microsoftonline.com/${this.tenantId}/oauth2/v2.0/authorize`, client_id: `${environment.secureAuthClientId}`, redirect_uri: `${this.getBaseUrl()}/auth-callback`, post_logout_redirect_uri: this.getBaseUrl(), response_type: 'id_token', scope: 'openid profile email', filterProtocolClaims: true, loadUserInfo: false, automaticSilentRenew: false, checkSessionInterval: 1000000, userStore: new WebStorageStateStore({ store: sessionStorage }) }; }; return this.http.get<{ keys }>(this.jwks_uri).pipe( map(val => add_signing_keys(val.keys)) ); }
内容的提问来源于stack exchange,提问作者sreddy
相关产品推荐
相关产品推荐

