最新版Loopback中Project模型细粒度ACL配置方案咨询
配置Loopback多项目架构的ACL权限
嘿,刚好我之前在Loopback里实现过几乎一模一样的多项目权限控制,给你一步步拆解怎么弄:
第一步:梳理并定义模型关系
先别急着写ACL,得把User、Project之间的关联关系搭好,这是权限生效的基础:
- 所有者关系:每个Project属于一个User(所有者),一个User可以拥有多个Project。在Project模型里添加
ownerId字段并定义关联:
同时在User模型里补上反向关系:// src/models/project.model.ts @belongsTo(() => User, {name: 'owner'}) ownerId: string;// src/models/user.model.ts @hasMany(() => Project, {keyTo: 'ownerId'}) ownedProjects: Project[]; - 成员关系:支持用户加入多个项目、项目拥有多个成员,这里推荐用
through关系(比hasAndBelongsToMany更灵活,方便后续扩展成员权限):- 先创建
ProjectMember中间模型,包含userId和projectId两个关联字段 - 在User模型里添加:
@hasMany(() => Project, {through: () => ProjectMember}) memberOfProjects: Project[]; - 在Project模型里添加:
@hasMany(() => User, {through: () => ProjectMember}) members: User[];
- 先创建
第二步:定义动态角色
Loopback默认的$owner角色只针对模型本身的所有者(比如Project的owner),但我们需要一个能判断用户是否是项目成员的动态角色:
- 创建角色解析器文件
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; } - 在应用启动时注册这个角色:
// 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
相关产品推荐
相关产品推荐

