delon-acl访问控制列表Skill delon-acl

@delon/acl 是一个基于角色的访问控制(RBAC)系统,专为 Angular 应用程序设计,用于管理用户权限和UI元素可见性。该技能提供权限管理服务、条件性UI指令和路由守卫,支持多租户架构和复杂权限逻辑,适用于企业级应用的前端权限控制。关键词:Angular权限控制、RBAC系统、访问控制列表、UI权限管理、前端安全、角色权限、ACL指令、路由守卫、多租户权限、企业应用安全。

前端开发 0 次安装 0 次浏览 更新于 2/28/2026

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) 开发团队