name: delon-acl description: ‘@delon/acl 技能 - 基于角色的权限控制和UI元素可见性的访问控制列表。用于 ng-events 建筑工地进度跟踪系统。’
@delon/acl - 访问控制列表
触发模式:“ACL”、“permission”、“role”、“access control”、“@delon/acl”、“can”、“ability”
概述
@delon/acl 为 ng-alain 应用程序提供基于角色的访问控制(RBAC)系统,根据用户角色和权限控制UI元素可见性和功能访问。
包:@delon/acl@20.1.0
集成:@delon/auth 用于身份验证
核心概念
1. ACLService - 权限管理
import { inject } from '@angular/core';
import { ACLService } from '@delon/acl';
@Component({
selector: 'app-dashboard',
standalone: true
})
export class DashboardComponent {
private aclService = inject(ACLService);
ngOnInit(): void {
// 设置用户角色和权限
this.aclService.setRole(['admin', 'user']);
this.aclService.setAbility(['task:create', 'task:edit', 'task:delete']);
// 设置完整的ACL配置
this.aclService.set({
role: ['admin'],
ability: ['task:create', 'task:edit'],
mode: 'oneOf' // 'allOf' 或 'oneOf'
});
}
canEditTask(): boolean {
return this.aclService.can('task:edit');
}
isAdmin(): boolean {
return this.aclService.can({ role: ['admin'] });
}
}
2. ACL 指令 - 条件性UI
*aclIf 指令
import { ACLIfDirective } from '@delon/acl';
@Component({
imports: [ACLIfDirective],
template: `
<!-- 仅当用户具有管理员角色时显示 -->
<button *aclIf="'admin'" nz-button nzType="primary">
管理设置
</button>
<!-- 仅当用户具有 task:create 权限时显示 -->
<button *aclIf="'task:create'" nz-button>
创建任务
</button>
<!-- 复杂条件:管理员 OR task:delete 权限 -->
<button *aclIf="deletePermission" nz-button nzDanger>
删除
</button>
`
})
export class TaskListComponent {
deletePermission = {
role: ['admin'],
ability: ['task:delete'],
mode: 'oneOf'
};
}
使用 @if 的 ACL(Angular 20)
@Component({
template: `
@if (canCreate()) {
<button nz-button (click)="createTask()">
创建任务
</button>
}
@if (isAdmin()) {
<nz-card>
<h3>管理员面板</h3>
<!-- 仅管理员可见的内容 -->
</nz-card>
}
`
})
export class DashboardComponent {
private aclService = inject(ACLService);
canCreate = signal(this.aclService.can('task:create'));
isAdmin = signal(this.aclService.can({ role: ['admin'] }));
}
3. ACL 守卫 - 路由保护
import { inject } from '@angular/core';
import { CanActivateFn } from '@angular/router';
import { ACLService } from '@delon/acl';
import { Router } from '@angular/router';
// 基于角色的守卫
export const adminGuard: CanActivateFn = () => {
const aclService = inject(ACLService);
const router = inject(Router);
if (aclService.can({ role: ['admin'] })) {
return true;
}
return router.parseUrl('/403');
};
// 基于权限的守卫
export const taskCreateGuard: CanActivateFn = () => {
const aclService = inject(ACLService);
const router = inject(Router);
if (aclService.can('task:create')) {
return true;
}
return router.parseUrl('/403');
};
// 复杂权限守卫
export const blueprintEditGuard: CanActivateFn = () => {
const aclService = inject(ACLService);
const router = inject(Router);
const canEdit = aclService.can({
role: ['admin', 'owner'],
ability: ['blueprint:edit'],
mode: 'oneOf'
});
return canEdit || router.parseUrl('/403');
};
路由配置:
import { Routes } from '@angular/router';
export const routes: Routes = [
{
path: 'admin',
canActivate: [adminGuard],
loadComponent: () => import('./admin/admin.component')
},
{
path: 'tasks/create',
canActivate: [taskCreateGuard],
loadComponent: () => import('./tasks/create.component')
},
{
path: 'blueprints/:id/edit',
canActivate: [blueprintEditGuard],
loadComponent: () => import('./blueprints/edit.component')
}
];
实际蓝图集成
ng-events(GigHub) 的权限结构
import { Injectable, inject } from '@angular/core';
import { ACLService } from '@delon/acl';
import { AuthService } from '@core/services/auth.service';
import { BlueprintMemberRepository } from '@core/data-access/blueprint-member.repository';
/**
* ng-events(GigHub) 权限格式:
* - 角色:'owner'、'admin'、'member'、'viewer'
* - 权限:'模块:操作' 格式
* 示例:'task:create'、'task:edit'、'task:delete'
* 'blueprint:edit'、'member:invite'
*/
@Injectable({ providedIn: 'root' })
export class PermissionService {
private aclService = inject(ACLService);
private authService = inject(AuthService);
private memberRepo = inject(BlueprintMemberRepository);
/**
* 初始化蓝图的权限
*/
async initBlueprintPermissions(blueprintId: string): Promise<void> {
const userId = this.authService.currentUserId();
if (!userId) {
this.aclService.set({ role: [], ability: [] });
return;
}
// 获取用户的成员资格
const member = await this.memberRepo.findByUserAndBlueprint(
userId,
blueprintId
);
if (!member || member.status !== 'active') {
this.aclService.set({ role: [], ability: [] });
return;
}
// 设置角色和权限
this.aclService.set({
role: [member.role],
ability: member.permissions || [],
mode: 'oneOf'
});
}
/**
* 检查用户是否可以执行操作
*/
can(ability: string): boolean {
return this.aclService.can(ability);
}
/**
* 检查用户是否具有角色
*/
hasRole(role: string | string[]): boolean {
return this.aclService.can({ role: Array.isArray(role) ? role : [role] });
}
/**
* 检查用户是否可以编辑任务
*/
canEditTask(task: Task): boolean {
// 所有者或管理员始终可以编辑
if (this.hasRole(['owner', 'admin'])) {
return true;
}
// 成员如果有权限且被分配则可以编辑
return this.can('task:edit') &&
task.assignedTo === this.authService.currentUserId();
}
/**
* 检查用户是否可以删除任务
*/
canDeleteTask(): boolean {
return this.hasRole(['owner', 'admin']) || this.can('task:delete');
}
}
具有蓝图权限的组件
import { Component, signal, computed, inject, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { PermissionService } from '@core/services/permission.service';
import { TaskService } from '@core/services/task.service';
import { ACLIfDirective } from '@delon/acl';
@Component({
selector: 'app-task-detail',
standalone: true,
imports: [ACLIfDirective],
template: `
<nz-card>
<h2>{{ task()?.title }}</h2>
<!-- 如果用户可以编辑则显示编辑按钮 -->
@if (canEdit()) {
<button nz-button (click)="editTask()">
编辑任务
</button>
}
<!-- 如果用户可以删除则显示删除按钮 -->
@if (canDelete()) {
<button nz-button nzDanger (click)="deleteTask()">
删除任务
</button>
}
<!-- 替代方案:使用 *aclIf 指令 -->
<button *aclIf="'task:edit'" nz-button>
编辑
</button>
<!-- 如果用户是所有者/管理员则显示管理面板 -->
@if (isOwnerOrAdmin()) {
<nz-card>
<h3>管理面板</h3>
<!-- 管理员功能 -->
</nz-card>
}
</nz-card>
`
})
export class TaskDetailComponent implements OnInit {
private route = inject(ActivatedRoute);
private taskService = inject(TaskService);
private permissionService = inject(PermissionService);
task = signal<Task | null>(null);
canEdit = computed(() => {
const t = this.task();
return t ? this.permissionService.canEditTask(t) : false;
});
canDelete = computed(() =>
this.permissionService.canDeleteTask()
);
isOwnerOrAdmin = computed(() =>
this.permissionService.hasRole(['owner', 'admin'])
);
async ngOnInit(): Promise<void> {
const taskId = this.route.snapshot.params['id'];
const blueprintId = this.route.snapshot.params['blueprintId'];
// 为此蓝图初始化权限
await this.permissionService.initBlueprintPermissions(blueprintId);
// 加载任务
const task = await this.taskService.getTask(taskId);
this.task.set(task);
}
editTask(): void {
// 实现
}
deleteTask(): void {
// 实现
}
}
最佳实践
1. 尽早初始化 ACL
✅ 应该做:在应用初始化或路由解析器中初始化
// app.config.ts
export const appConfig: ApplicationConfig = {
providers: [
provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(routes),
{
provide: APP_INITIALIZER,
useFactory: (acl: ACLService, auth: AuthService) => () => {
// 身份验证后初始化 ACL
return auth.getCurrentUser().then(user => {
if (user) {
acl.set({
role: [user.role],
ability: user.permissions
});
}
});
},
deps: [ACLService, AuthService],
multi: true
}
]
};
2. 使用信号与 ACL
✅ 应该做:创建响应式权限检查
canCreate = computed(() => this.aclService.can('task:create'));
isAdmin = computed(() => this.aclService.can({ role: ['admin'] }));
3. 结合安全规则
✅ 应该做:使用 ACL 进行 UI 控制 + Firestore 安全规则进行数据保护
// UI:如果没有权限则隐藏按钮
@if (permissionService.can('task:delete')) {
<button (click)="delete()">删除</button>
}
// Firestore 安全规则:强制执行权限
match /tasks/{taskId} {
allow delete: if isBlueprintMember(resource.data.blueprintId)
&& hasPermission(resource.data.blueprintId, 'task:delete');
}
集成检查清单
- [ ] 安装 @delon/acl@20.1.0
- [ ] 在 APP_INITIALIZER 中初始化 ACL
- [ ] 定义角色和权限结构
- [ ] 与 @delon/auth 集成
- [ ] 为受保护路由创建路由守卫
- [ ] 使用 *aclIf 或 @if 与 can() 进行 UI 控制
- [ ] 结合 Firestore 安全规则
- [ ] 测试权限边界情况
反模式
❌ 在组件中硬编码权限:
if (user.role === 'admin') { /* ... */ }
✅ 使用 ACL 服务:
if (this.aclService.can({ role: ['admin'] })) { /* ... */ }
❌ 仅客户端权限检查:
// 仅 UI - 不安全!
@if (canDelete()) { <button (click)="delete()">删除</button> }
✅ 客户端 + 服务器验证:
// UI 检查
@if (canDelete()) { <button (click)="delete()">删除</button> }
// Firestore 安全规则
allow delete: if hasPermission('task:delete');
交叉引用
- delon-auth - 身份验证集成
- firestore-security-rules - 服务器端验证
- blueprint-integration - 多租户权限
.github/instructions/ng-ng-events(GigHub)-architecture.instructions.md- 权限架构
版本:1.0
创建日期:2025-12-25
维护者:ng-events(GigHub) 开发团队