You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

最新版Loopback中Project模型细粒度ACL配置方案咨询

配置Loopback多项目架构的ACL权限

嘿,刚好我之前在Loopback里实现过几乎一模一样的多项目权限控制,给你一步步拆解怎么弄:

第一步:梳理并定义模型关系

先别急着写ACL,得把User、Project之间的关联关系搭好,这是权限生效的基础:

  • 所有者关系:每个Project属于一个User(所有者),一个User可以拥有多个Project。在Project模型里添加ownerId字段并定义关联:
    // src/models/project.model.ts
    @belongsTo(() => User, {name: 'owner'})
    ownerId: string;
    
    同时在User模型里补上反向关系:
    // src/models/user.model.ts
    @hasMany(() => Project, {keyTo: 'ownerId'})
    ownedProjects: Project[];
    
  • 成员关系:支持用户加入多个项目、项目拥有多个成员,这里推荐用through关系(比hasAndBelongsToMany更灵活,方便后续扩展成员权限):
    1. 先创建ProjectMember中间模型,包含userId和projectId两个关联字段
    2. 在User模型里添加:
      @hasMany(() => Project, {through: () => ProjectMember})
      memberOfProjects: Project[];
      
    3. 在Project模型里添加:
      @hasMany(() => User, {through: () => ProjectMember})
      members: User[];
      

第二步:定义动态角色

Loopback默认的$owner角色只针对模型本身的所有者(比如Project的owner),但我们需要一个能判断用户是否是项目成员的动态角色:

  1. 创建角色解析器文件src/roles/project-member.resolver.ts:
    import {AuthorizationContext, AuthorizationDecision, AuthorizationMetadata} from '@loopback/authorization';
    import {securityId, UserProfile} from '@loopback/security';
    
    export async function projectMemberResolver(
      ctx: AuthorizationContext,
      metadata: AuthorizationMetadata,
    ): Promise<AuthorizationDecision> {
      // 获取当前登录用户信息
      const user = ctx.principals[0]?.userProfile as UserProfile;
      if (!user) return AuthorizationDecision.DENY;
    
      // 针对单个项目操作(如findById),判断用户是否在成员列表中
      const project = ctx.invocationContext.args[0];
      if (project && project.members) {
        const memberIds = project.members.map(m => m[securityId]);
        if (memberIds.includes(user[securityId])) {
          return AuthorizationDecision.ALLOW;
        }
      }
      return AuthorizationDecision.DENY;
    }
    
  2. 在应用启动时注册这个角色:
    // src/application.ts
    import {AuthorizationComponent} from '@loopback/authorization';
    import {projectMemberResolver} from './roles/project-member.resolver';
    
    this.component(AuthorizationComponent);
    this.configure(AuthorizationBindings.COMPONENT).to({
      roleResolvers: {
        projectMember: projectMemberResolver,
      },
    });
    

第三步:配置核心ACL规则

在Project模型的acl数组里按优先级从高到低添加规则:

// src/models/project.model.ts
@model({
  settings: {
    acl: [
      // 1. 管理员拥有所有操作权限
      {
        accessType: '*',
        principalType: 'ROLE',
        principalId: '$admin',
        permission: 'ALLOW',
      },
      // 2. 项目所有者拥有对自己项目的全权限
      {
        accessType: '*',
        principalType: 'ROLE',
        principalId: '$owner',
        permission: 'ALLOW',
      },
      // 3. 项目成员拥有查看项目的权限
      {
        accessType: 'READ',
        principalType: 'ROLE',
        principalId: 'projectMember',
        permission: 'ALLOW',
        property: ['findById', 'findOne'],
      },
      // 4. 禁止未授权用户访问任何内容
      {
        accessType: '*',
        principalType: 'ROLE',
        principalId: '$unauthenticated',
        permission: 'DENY',
      },
    ],
  },
})

第四步:处理列表查询的数据过滤

上面的ACL能控制单个项目的访问,但成员调用find(列表查询)时,需要自动过滤出自己加入的项目,这一步要重写ProjectRepository的find方法:

// src/repositories/project.repository.ts
import {Filter, repository} from '@loopback/repository';
import {Project} from '../models';
import {inject, Getter} from '@loopback/core';
import {SecurityBindings, UserProfile} from '@loopback/security';
import {securityId} from '@loopback/security';

export class ProjectRepository extends DefaultCrudRepository<...> {
  constructor(
    @inject(SecurityBindings.USER, {optional: true}) private userProfile: UserProfile,
    // 其他注入项...
  ) {
    super(Project, dataSource);
  }

  async find(filter?: Filter<Project>): Promise<Project[]> {
    // 管理员直接返回所有项目
    const isAdmin = this.userProfile?.roles?.includes('admin');
    if (isAdmin) {
      return super.find(filter);
    }

    // 普通成员只返回自己加入的项目
    const userId = this.userProfile?.[securityId];
    const modifiedFilter = {
      ...filter,
      where: {
        ...filter?.where,
        members: {id: userId},
      },
    };
    return super.find(modifiedFilter);
  }
}

额外细节:成员管理权限

如果需要让项目所有者/管理员能添加、移除成员,可以在自定义的远程方法(比如addMember、removeMember)上单独配置ACL:

// src/models/project.model.ts
@remoteMethod()
async addMember(userId: string): Promise<void> {
  // 添加成员的业务逻辑
}

// 在ACL数组中添加规则
{
  accessType: 'EXECUTE',
  principalType: 'ROLE',
  principalId: '$owner',
  permission: 'ALLOW',
  property: 'addMember',
},
{
  accessType: 'EXECUTE',
  principalType: 'ROLE',
  principalId: '$admin',
  permission: 'ALLOW',
  property: 'addMember',
},

这样一套配置下来,就能完美实现你要的权限逻辑:管理员通吃所有内容,所有者管理自己的项目,成员只能查看自己加入的项目~

内容的提问来源于stack exchange,提问作者Alberto

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 04:09:15