Angular双路由守卫同时启用时相互阻塞无法正常重定向问题
问题原因
两个守卫同时配置后出现导航拦截、无法重定向,本质是重定向死锁加上路由配置遗漏:
- 未登录时访问受保护路由,
AuthGuard会跳转到/welcome,如果此时WelcomeGuard的判断逻辑反向触发跳回/,Angular在单次导航周期中检测到循环重定向会直接终止导航,既不渲染页面也不执行跳转 - 现有配置里
main-regulation、user-dashboard、admin-dashboard三个需要登录权限的路由没有挂载AuthGuard,本身就存在鉴权漏洞 - 如果
isUserLoggedIn$是长期持有订阅的流,没有在鉴权完成后结束订阅,Angular路由会一直等待守卫返回结果,也会导致导航卡住
修复方案
1. 修正路由配置
补全所有受保护路由的鉴权守卫,增加通配符路由兜底:
const routes: Routes = [ { path: 'main-regulation', canActivate: [AuthGuard], loadChildren: () => import('./modules/main-regulation/main-regulation.module').then(m => m.MainRegulationModule) }, { path: '', canActivate: [AuthGuard], loadChildren: () => import('./modules/main-page/main-page.module').then(m => m.MainPageModule), }, { path: 'user-dashboard', canActivate: [AuthGuard], component: UserDashboardComponent }, { path: 'admin-dashboard', canActivate: [AuthGuard], component: AdminDashboardComponent }, { path: 'welcome', canActivate: [WelcomeGuard], loadChildren: () => import('./modules/welcome/welcome.module').then(m => m.WelcomeModule), }, { path: '**', redirectTo: '' } ];
2. 优化守卫逻辑,避免循环重定向
给两个守卫增加当前路径判断,已经在重定向目标路径时直接返回false终止导航,不要重复触发跳转;同时给登录状态流加take(1),拿到当前登录状态后立即结束流,避免路由等待。
修正后的AuthGuard:
import { Injectable } from '@angular/core'; import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTree } from '@angular/router'; import { map, Observable, take } from 'rxjs'; import { LoginService } from "../app/services/login.service"; @Injectable({ providedIn: 'root' }) export class AuthGuard implements CanActivate { constructor(private loginService: LoginService, private router: Router) {} canActivate( route: ActivatedRouteSnapshot, state: RouterStateSnapshot ): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree { return this.loginService.isUserLoggedIn$.pipe( take(1), map(isLoggedIn => { if (isLoggedIn) return true; // 已经在欢迎页时不再重复跳转 if (state.url === '/welcome') return false; return this.router.parseUrl('/welcome'); }) ); } }
修正后的WelcomeGuard:
import { Injectable } from '@angular/core'; import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTree } from '@angular/router'; import { map, Observable, take } from 'rxjs'; import { LoginService } from "../app/services/login.service"; @Injectable({ providedIn: 'root' }) export class WelcomeGuard implements CanActivate { constructor(private readonly loginService: LoginService, private readonly router: Router) {} canActivate( route: ActivatedRouteSnapshot, state: RouterStateSnapshot ): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree { return this.loginService.isUserLoggedIn$.pipe( take(1), map(isLoggedIn => { if (!isLoggedIn) return true; // 已经在首页时不再重复跳转 if (state.url === '/') return false; return this.router.parseUrl('/'); }) ); } }
注意事项
不要用canLoad替代canActivate做登录鉴权:canLoad只会在懒加载模块首次加载时执行一次,模块加载完成后后续访问路由不会再触发校验,存在权限绕过风险。
内容的提问来源于stack exchange,提问作者Kacper Czerwiński
相关产品推荐
相关产品推荐

