Angular14中如何实现基于角色的菜单与路由控制?
Angular14 基于角色的菜单实现方案
一、核心思路
先明确各角色对应的菜单权限(可根据实际需求调整):
- Admin:Register、Login、Add New Employee、List of employees
- Employee:Login、List of employees
- Support:Login、List of employees、Add New Employee
通过登录后存储用户角色,在头部组件中根据角色动态过滤并渲染菜单,同时配合路由守卫防止无权限用户直接通过URL访问页面。
二、具体实现步骤
1. 完善用户认证服务(AuthService)
用响应式方式管理用户角色,确保菜单能实时更新:
// auth.service.ts import { Injectable } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class AuthService { private currentUserRoleSubject = new BehaviorSubject<string | null>(null); currentUserRole$ = this.currentUserRoleSubject.asObservable(); // 登录成功后存储角色 setUserRole(role: string) { this.currentUserRoleSubject.next(role); localStorage.setItem('userRole', role); // 持久化存储 } // 初始化时从本地存储读取角色 initUserRole() { const role = localStorage.getItem('userRole'); if (role) this.currentUserRoleSubject.next(role); } // 登出清除角色 logout() { this.currentUserRoleSubject.next(null); localStorage.removeItem('userRole'); } }
2. 头部组件动态渲染菜单
在头部组件中订阅角色变化,过滤出当前角色可访问的菜单:
// header.component.ts import { Component, OnInit } from '@angular/core'; import { AuthService } from '../services/auth.service'; interface MenuItem { label: string; route: string; allowedRoles: string[]; } @Component({ selector: 'app-header', templateUrl: './header.component.html', styleUrls: ['./header.component.css'] }) export class HeaderComponent implements OnInit { menuItems: MenuItem[] = [ { label: 'Register', route: '/register', allowedRoles: ['Admin'] }, { label: 'Login', route: '/login', allowedRoles: ['Admin', 'Employee', 'Support'] }, { label: 'Add New Employee', route: '/add-employee', allowedRoles: ['Admin', 'Support'] }, { label: 'List of employees', route: '/employees', allowedRoles: ['Admin', 'Employee', 'Support'] } ]; filteredMenuItems: MenuItem[] = []; constructor(private authService: AuthService) {} ngOnInit(): void { this.authService.initUserRole(); this.authService.currentUserRole$.subscribe(role => { if (!role) { // 未登录仅显示Login this.filteredMenuItems = this.menuItems.filter(item => item.label === 'Login'); return; } this.filteredMenuItems = this.menuItems.filter(item => item.allowedRoles.includes(role)); }); } }
头部组件模板:
<!-- header.component.html --> <nav> <ul> <li *ngFor="let item of filteredMenuItems"> <a [routerLink]="item.route">{{ item.label }}</a> </li> <li *ngIf="authService.currentUserRole$ | async"> <button (click)="authService.logout()">Logout</button> </li> </ul> </nav>
3. 路由守卫控制页面访问(可选但推荐)
防止用户直接通过URL访问无权限页面:
// role.guard.ts import { Injectable } from '@angular/core'; import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router'; import { AuthService } from './auth.service'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; @Injectable({ providedIn: 'root' }) export class RoleGuard implements CanActivate { constructor(private authService: AuthService, private router: Router) {} canActivate( route: ActivatedRouteSnapshot, state: RouterStateSnapshot ): Observable<boolean> { return this.authService.currentUserRole$.pipe( map(role => { const allowedRoles = route.data['allowedRoles'] as string[]; if (!role) { this.router.navigate(['/login']); return false; } if (allowedRoles.includes(role)) return true; // 无权限跳转到首页或专属无权限页面 this.router.navigate(['/']); return false; }) ); } }
路由配置中使用守卫:
// app-routing.module.ts import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { RegisterComponent } from './register/register.component'; import { LoginComponent } from './login/login.component'; import { AddEmployeeComponent } from './add-employee/add-employee.component'; import { EmployeeListComponent } from './employee-list/employee-list.component'; import { RoleGuard } from './services/role.guard'; const routes: Routes = [ { path: 'register', component: RegisterComponent, canActivate: [RoleGuard], data: { allowedRoles: ['Admin'] } }, { path: 'login', component: LoginComponent }, { path: 'add-employee', component: AddEmployeeComponent, canActivate: [RoleGuard], data: { allowedRoles: ['Admin', 'Support'] } }, { path: 'employees', component: EmployeeListComponent, canActivate: [RoleGuard], data: { allowedRoles: ['Admin', 'Employee', 'Support'] } }, { path: '', redirectTo: '/login', pathMatch: 'full' } ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule {}
三、注意事项
- 优先使用
currentUserRole$Observable实现响应式菜单更新,确保角色变化时菜单实时刷新 - 不能仅依赖前端菜单隐藏,必须配合路由守卫拦截非法访问
- 若涉及敏感场景,建议后端返回用户角色信息,前端避免直接存储敏感数据,或使用HttpOnly Cookie管理登录状态
内容的提问来源于stack exchange,提问作者its me
相关产品推荐
相关产品推荐

