Nest.js集成Casl时集合读取权限校验不符合预期问题
Nest.js集成Casl时集合读取权限校验不符合预期问题
我完全懂你遇到的困扰——你本来想让匿名用户只能读取公开的Collection,结果调用ability.can(Action.READ, Collection)(检查整个Collection类型的权限)时却返回了true,完全不符合预期。
问题根源
你现在写的规则allow(Action.READ, Collection, { isPublic: true })是实例级的权限规则,它只定义了“允许读取isPublic为true的单个Collection实例”。但当你用Collection类作为subject调用can方法时,这是在检查对整个资源类型的权限。Casl的默认逻辑是:只要存在针对该类型的带条件允许规则,就会认为“存在符合条件的资源,所以类型级权限为true”,但这和你想要的“默认拒绝,只有公开实例才允许”逻辑刚好相反。
解决方案
只需要调整规则的顺序,先默认拒绝所有对Collection类型的READ权限,再开放符合条件的实例权限:
@Injectable() export class AuthorizationAbilityFactory { createForUser(user?: JwtPayload) { const { can: allow, cannot: deny, build, } = new AbilityBuilder<AppAbility>(createMongoAbility); // 先默认拒绝所有对Collection类型的READ权限 deny(Action.READ, Collection); // 再允许读取isPublic为true的Collection实例 allow(Action.READ, Collection, { isPublic: true }).because( 'Public collections can be read', ); allow(Action.READ, Media); if (user?.role !== Role.USER) { allow(Action.CREATE, User); } return build({ detectSubjectType: (item) => item.constructor as ExtractSubjectType<Subjects>, }); } }
效果验证
修改后再跑你的测试:
const ability = authorizationAbilityFactory.createForUser(); // 匿名用户 expect(ability.can(Action.READ, Collection)).toBe(false); // 现在会顺利通过 // 同时检查具体实例的权限也符合预期 expect(ability.can(Action.READ, { isPublic: true, constructor: Collection })).toBe(true); expect(ability.can(Action.READ, { isPublic: false, constructor: Collection })).toBe(false);
额外实用提示
在实际业务的列表查询场景中,除了做类型级的权限检查,你还可以用Casl的buildQuery方法生成数据库查询条件,自动过滤出用户有权限访问的资源,比如:
const queryConditions = ability.buildQuery(Action.READ, Collection); // 把这个条件传给数据库查询(比如MongoDB的find方法),只会返回公开的Collection
备注:内容来源于stack exchange,提问作者Donovan BENFOUZARI
相关产品推荐
相关产品推荐

