Angular Router canActivate守卫如何实现实时权限校验?
Angular 权限守卫:权限变更后自动触发校验并跳转
问题背景
我们可以在Angular RouterModule中使用Guards,通过实现canActivate()方法校验模块访问权限,该方法借助业务逻辑执行校验并返回布尔类型Observable。
现有场景:用户访问受CheckAccessGuard保护的Private Page Module,canActivate()返回服务变量haveAccess的Observable——返回true允许访问,false则跳转至403页面。但在模块内点击按钮将haveAccess设为false后,并未触发跳转,原因是canActivate守卫仅在路由进入前执行校验,进入模块后不再监听权限变化。
需求:无需修改Private Page Module代码,仅通过守卫逻辑实现权限变化时自动校验并跳转至403页面。
现有示例代码:
路由配置
const routes: Routes = [ { path: 'private-area', loadChildren: () => import('./pages/private-page.module').then((m) => m.PrivatePageModule), canActivate: [CheckAccessGuard], } ]
CheckAccessGuard
... constructor(private checkAccessService: CheckAccessService) {} canActivate(): Observable<boolean> { return of(this.checkAccess()) } checkAccess(): boolean { return this.checkAccessService.haveAccess; } ...
CheckAccessService
export class CheckAccessService { public haveAccess = true; }
PrivatePageModule 默认组件
<!-- .html --> <button (click)="checkAccessService.haveAccess = false">取消权限</button>
// .ts constructor(public checkAccessService: CheckAccessService) {}
解决方案
要实现权限变更后的自动校验跳转,需要让守卫监听权限状态的变化,同时改造服务的状态存储方式为可观察对象:
1. 改造CheckAccessService:使用BehaviorSubject存储权限状态
把普通变量改成BehaviorSubject,既保留原有get/set语法兼容组件代码,又能提供可订阅的状态流:
import { Injectable } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class CheckAccessService { private readonly _haveAccess = new BehaviorSubject<boolean>(true); // 保留原有get/set语法,兼容组件直接赋值 public get haveAccess(): boolean { return this._haveAccess.value; } public set haveAccess(value: boolean) { this._haveAccess.next(value); } // 对外暴露可订阅的Observable public haveAccess$ = this._haveAccess.asObservable(); }
2. 改造CheckAccessGuard:监听权限变化并触发跳转
在守卫中订阅权限状态,当状态变为false时触发403跳转;同时调整canActivate返回值,确保路由进入时的校验正常工作:
import { Injectable } from '@angular/core'; import { CanActivate, Router } from '@angular/router'; import { Observable, takeUntil, Subject } from 'rxjs'; import { CheckAccessService } from './check-access.service'; @Injectable({ providedIn: 'root' }) export class CheckAccessGuard implements CanActivate { private readonly destroy$ = new Subject<void>(); constructor( private checkAccessService: CheckAccessService, private router: Router ) { // 订阅权限状态变化,触发跳转逻辑 this.checkAccessService.haveAccess$ .pipe(takeUntil(this.destroy$)) .subscribe((hasAccess) => { if (!hasAccess) { this.router.navigate(['/403']); } }); } canActivate(): Observable<boolean> { // 返回当前权限状态的Observable,确保路由进入时的校验 return this.checkAccessService.haveAccess$; } // 清理订阅,避免内存泄漏 ngOnDestroy(): void { this.destroy$.next(); this.destroy$.complete(); } }
3. 验证效果
无需修改PrivatePageModule的任何代码,点击按钮将haveAccess设为false后,守卫会立即监听到状态变化,自动跳转至403页面。
关键说明
BehaviorSubject既保留了原有组件的赋值逻辑,又能提供状态变化的订阅能力,无需修改私有模块代码。- 守卫内的订阅通过
takeUntil和销毁信号清理,避免内存泄漏。 canActivate直接返回权限状态的Observable,确保路由进入时的初始校验正常执行。
内容的提问来源于stack exchange,提问作者Sergej
相关产品推荐
相关产品推荐

