使用CASL进行权限校验时抛出运行时错误的问题求助
问题描述
我在中间件中配置CASL权限规则,目的是允许已认证用户访问同一公寓(unitId)下的其他用户,规则代码如下:
can(DirectoryAction.VIEW, 'DirectoryUser', { roles: { some: { role: { unitId: CASL_ROLE.unitId } } } });
其中:
DirectoryAction是包含view、delete等操作的枚举DirectoryUser是目标对象名称CASL_ROLE指代已认证用户的角色信息unitId是公寓ID
代码编译无异常,但VS Code检测到TypeScript运行时错误:
"equals" does not supports comparison of arrays and objects
对应的Prisma模型定义如下:
model DirectoryUser { id BigInt @id @default(autoincrement()) userName String @map("user_name") password String roles DirectoryRoleUserMapping[] @@map("directory_users") } model DirectoryRoleUserMapping { id BigInt @id @default(autoincrement()) roleId BigInt @map("role_id") role DirectoryRole @relation(fields: [roleId], references: [id], onDelete: Cascade, onUpdate: Cascade) userId BigInt @map("user_id") user DirectoryUser @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade) @@unique([roleId, userId]) @@map("directory_role_user_mappings") } model DirectoryRole { id BigInt @id @default(autoincrement()) name String unitId BigInt? @map("unit_id") unit BmsUnit? @relation(fields: [unitId], references: [id], onDelete: Cascade, onUpdate: Cascade) rank BigInt users DirectoryRoleUserMapping[] @@unique([name, unitId]) @@map("directory_roles") }
如果简化条件为{ roles: { some: { roleId: CASL_ROLE.id } } }则无错误,但这不符合业务需求。我发现只要在some中使用嵌套对象而非简单键值对就会触发该错误,求排查解决。
问题根源
CASL默认会将{ role: { unitId: CASL_ROLE.unitId } }解析为整个role对象等于{ unitId: CASL_ROLE.unitId },而非role对象的unitId字段等于目标值。这种对象级别的比较会触发Prisma的equals操作,但Prisma不支持用equals直接比较对象/数组,因此抛出错误。
解决方法
方法1:显式指定字段比较操作符
通过显式使用Prisma的字段比较语法,明确告诉CASL要比较的是role对象下的unitId字段,而非整个对象:
can(DirectoryAction.VIEW, 'DirectoryUser', { roles: { some: { role: { unitId: { equals: CASL_ROLE.unitId } } } } });
方法2:使用函数式规则(更灵活)
如果嵌套查询的类型推断仍有问题,可以改用CASL的函数式规则,直接返回符合Prisma语法的查询条件,绕开类型推断限制:
can(DirectoryAction.VIEW, 'DirectoryUser', (_, { user }) => { const targetUnitId = user.CASL_ROLE.unitId; return { roles: { some: { role: { unitId: targetUnitId } } } }; });
方法3:检查CASL与Prisma适配器版本
确保你使用的@casl/prisma适配器版本与Prisma版本兼容,旧版本的适配器可能对嵌套查询的类型支持不完善。可以尝试升级到最新稳定版:
npm update @casl/prisma prisma
验证
修改后,TypeScript错误应该消失,同时权限规则会正确匹配同一unitId下的用户:只要当前用户的角色所属公寓ID,与目标用户的任意角色所属公寓ID一致,就允许访问。
内容的提问来源于stack exchange,提问作者Anne Nonyme

