@casl/mongoose

中文文档
9.1.0发布于 2026/10/28MITTypeScriptESM + CJS零依赖构建来源已验证近 30 天下载 21.9万

允许根据 CASL 规则向 MongoDB 查询可访问的记录

中文使用文档
文档目录8 节
  1. 安装
  2. 使用量
  3. accessibleBy 帮助者
  4. 可访问的记录插件
  5. 可访问字段By
  6. 在mongoose中支持脚本类型
  7. 想帮忙吗?
  8. 许可证

卡塞尔蒙哥

@casl/mongoose NPM version Support

此软件包集成 卡塞尔 财务报告和审定财务报表 蒙戈DB,换句话说,它允许根据CASL规则从MongoDB获取记录并回答诸如"哪些记录可以读取"或"哪些记录可以更新"之类的问题.

安装

npm install @casl/mongoose @casl/ability
# or
yarn add @casl/mongoose @casl/ability
# or
pnpm add @casl/mongoose @casl/ability

使用量

@casl/mongoose 不仅可以与 鸽子 但与任何 蒙戈DB 联合来文司机 accessibleBy 帮助函数 .

accessibleBy 帮助者

这种整洁的帮助器功能可以将能力规则转换为MongoDB查询,只从数据库取取取可访问的记录. 它可以用来与绵羊或 MongoDB 适配器数字 :

MongoDB 适配器

const { accessibleBy } = require('@casl/mongoose');
const { MongoClient } = require('mongodb');
const ability = require('./ability');

async function main() {
  const db = await MongoClient.connect('mongodb://localhost:27017/blog');
  let posts;

  try {
    posts = await db.collection('posts').find(accessibleBy(ability, 'update').ofType('Post'));
  } finally {
    db.close();
  }

  console.log(posts);
}

这也可以与其他条件相结合,帮助: $and 运算符 :

posts = await db.collection('posts').find({
  $and: [
    accessibleBy(ability, 'update').ofType('Post'),
    { public: true }
  ]
});

重要的东西!:永远不要使用散开运算符(即, ...将下列条件结合起来: accessibleBy 由于您可能无意中覆盖了限制访问特定记录的属性:

// returns { authorId: 1 }
const permissionRestrictedConditions = accessibleBy(ability, 'update').ofType('Post');

// DANGER DO NOT DO THIS (see above use $and)
const query = {
  // This is bad and potentially wrong code
  ...permissionRestrictedConditions,
  authorId: 2
};

上面的情况,我们翻唱 authorId 基本上允许未经许可进入作者的 id = 2

如果没有定义特定动作/主题的权限, accessibleBy 将返回 { $expr: { $eq: [0, 1] } } 当它被发送到MongoDB时,数据库会返回一个空结果集.

乌鸦

const Post = require('./Post') // mongoose model
const ability = require('./ability') // defines Ability instance

async function main() {
  const accessiblePosts = await Post.find(accessibleBy(ability).ofType('Post'));
  console.log(accessiblePosts);
}

历史上, @casl/mongoose 本来是为了实现与 鸽子 但是现在我们重新调整它的方向 是更多的蒙戈DB 特定的包,因为复杂的工作 与mongoose类型 在TS.

可访问的记录插件

此插件已贬值, 建议使用的方法 accessibleBy 帮助函数。 。 。 .

accessibleRecordsPlugin 是一个添加的插件 accessibleBy (a) 探询方法以及梦游模型的静态方法。 我们可以在全球添加这个插件 :

const { accessibleRecordsPlugin } = require('@casl/mongoose');
const mongoose = require('mongoose');

mongoose.plugin(accessibleRecordsPlugin);

在调用前确保添加插件 mongoose.model(...) 方法。 Mongoose 不会在调用前创建的模型中添加全局插件 mongoose.plugin()。 。 。 .

或特定型号:

const mongoose = require('mongoose')
const { accessibleRecordsPlugin } = require('@casl/mongoose')

const Post = new mongoose.Schema({
  title: String,
  author: String
})

Post.plugin(accessibleRecordsPlugin)

module.exports = mongoose.model('Post', Post)

之后,我们可以使用 accessibleBy 方法打开 Post数字 :

const Post = require('./Post')
const ability = require('./ability') // defines Ability instance

async function main() {
  const accessiblePosts = await Post.accessibleBy(ability);
  console.log(accessiblePosts);
}

见 CASL 指南 学会如何定义能力

或在现有查询实例中:

const Post = require('./Post');
const ability = require('./ability');

async function main() {
  const accessiblePosts = await Post.find({ status: 'draft' })
    .accessibleBy(ability)
    .select('title');
  console.log(accessiblePosts);
}

accessibleBy 返回实例 mongoose.Query 也就是说你可以用任何 mongoose.Query方法(例如:, select, (中文(简体) ), limit, (中文(简体) ), sort) (中文(简体) ). 默认, accessibleBy 基于规则列表构建查询 read 动作,但我们可以通过提供第二个可选参数来改变这一点:

const Post = require('./Post');
const ability = require('./ability');

async function main() {
  const postsThatCanBeUpdated = await Post.accessibleBy(ability, 'update');
  console.log(postsThatCanBeUpdated);
}

accessibleBy 建在顶部 rulesToQuery 函数从 @casl/ability/extra读取 数据库查询能力 来了解它是如何运作的.

如果用户没有权限做特定动作, CASL 会丢弃 ForbiddenError 并且不会向MongoDB发送请求。 它还说 __forbiddenByCasl__: 1 附加安全条件.

例如,让我们找到所有用户可以删除的帖子(我们还没有定义删除的能力):

const { defineAbility } = require('@casl/ability');
const mongoose = require('mongoose');
const Post = require('./Post');

mongoose.set('debug', true);

const ability = defineAbility(can => can('read', 'Post', { private: false }));

async function main() {
  try {
    const posts = await Post.accessibleBy(ability, 'delete');
  } catch (error) {
    console.log(error) // ForbiddenError;
  }
}

我们还可以利用由此产生的条件: 管道合计数字 :

const Post = require('./Post');
const ability = require('./ability');

async function main() {
  const query = Post.accessibleBy(ability)
    .where({ status: 'draft' })
    .getQuery();
  const result = await Post.aggregate([
    {
      $match: {
        $and: [
          query,
          // other aggregate conditions
        ]
      }
    },
    // other pipelines here
  ]);
  console.log(result);
}

或进入 映射图数字 :

const Post = require('./Post');
const ability = require('./ability');

async function main() {
  const query = Post.accessibleBy(ability)
    .where({ status: 'draft' })
    .getQuery();
  const result = await Post.mapReduce({
    query: {
      $and: [
        query,
        // other conditions
      ]
    },
    map: () => emit(this.title, 1);
    reduce: (_, items) => items.length;
  });
  console.log(result);
}

可访问字段By

accessibleFieldsBy 允许为主题类型或具体主题实例检索可用的字段 :

import { accessibleFieldsBy } from '@casl/mongoose';
import { Post } from './models';

accessibleFieldsBy(ability).ofType('Post') // returns accessible fields for Post model
accessibleFieldsBy(ability).ofType(Post) // also possible to pass class if classes are used for rule definition
accessibleFieldsBy(ability).of(new Post()) // returns accessible fields for Post model

这个帮助器可以在我们只需要发送一个模型中可访问的部分响应时直接使用:

const pick = require('lodash/pick');
const { accessibleFieldsBy } = require('@casl/mongoose');
const Post = require('./Post');

app.get('/api/posts/:id', async (req, res) => {
  const post = await Post.accessibleBy(ability).findByPk(req.params.id);
  res.send(pick(post, accessibleFieldsBy(ability).of(post)));
});

当用模型类型调用时, accessibleFieldsBy 不考虑实例条件。 也一样 检查逻辑 作为 Ability是因为 can 方法。 让我们举个例子来回顾一下:

const { defineAbility } = require('@casl/ability');
const Post = require('./Post');

const ability = defineAbility((can) => {
  can('read', 'Post', ['title'], { private: true });
  can('read', 'Post', ['title', 'description'], { private: false });
});
const post = new Post({ private: true, title: 'Private post' });

accessibleFieldsBy(ability).ofType(Post); // ['title', 'description']
accessibleFieldsBy(ability).of(post); // ['title']

如您所见, 模式级访问返回所有可以读取的字段 。 同时,可读取此特性的例级访问返回字段 post 举个例子 所以除了减少应用软件和数据库之间的流量之外 通常没什么意义 把模型级访问的结果传入 mongoose.Query是因为 select 方法 因为你最终需要调用 accessibleFieldsBy 在每一个例子.

此助手已预配置以获取全部字段 Model.schema.paths。如果不需要,请定义您的自定义助手 :

import { AnyMongoAbility, Generics } from "@casl/ability";
import { AccessibleFields, GetSubjectTypeAllFieldsExtractor } from "@casl/ability/extra";
import mongoose from 'mongoose';

const getSubjectTypeAllFieldsExtractor: GetSubjectTypeAllFieldsExtractor = (type) => {
  /** custom implementation of returning all fields */
};

export function accessibleFieldsBy<T extends AnyMongoAbility>(
  ability: T,
  action: Parameters<T['rulesFor']>[0] = 'read'
): AccessibleFields<Extract<Generics<T>['abilities'], unknown[]>[1]> {
  return new AccessibleFields(ability, action, getSubjectTypeAllFieldsExtractor);
}

在mongoose中支持脚本类型

软件包被用TypeScript写出,这使得与插件和帮助器一起工作更容易,因为IDE提供了有用的提示. 让我们看看它的行动!

如果我们有 Post 可称为:

import mongoose from 'mongoose';

export interface Post extends mongoose.Document {
  title: string
  content: string
  published: boolean
}

const PostSchema = new mongoose.Schema<Post>({
  title: String,
  content: String,
  published: Boolean
});

export const Post = mongoose.model('Post', PostSchema);

延长 Post 模式为 accessibleBy 方法足以包含相应的插件(全球或本地在 Post)和相应的使用 Model 类型。 所以,让我们改变这个例子,所以它包括 accessibleRecordsPlugin数字 :

import { accessibleRecordsPlugin, AccessibleRecordModel } from '@casl/mongoose';

// all previous code, except last line

PostSchema.plugin(accessibleRecordsPlugin);

export const Post = mongoose.model<Post, AccessibleRecordModel<Post>>('Post', PostSchema);

// Now we can safely use `Post.accessibleBy` method.
Post.accessibleBy(/* parameters */)
Post.where(/* parameters */).accessibleBy(/* parameters */);

accessibleFieldsBy 不需要针对老鼠的种类。 您可以直接使用模型或文档:

import { accessibleFieldsBy } from '@casl/mongoose';

accessibleFieldsBy(ability).ofType(Post);

const post = new Post();
accessibleFieldsBy(ability).of(post);

如果您想要类似已删除插件的模型或文档实例方法, 请在周围创建一个小型的 app级包装 accessibleFieldsBy 这符合你自己的打字和现场曝光规则.

想帮忙吗?

想要提交错误, 提供一些代码, 还是改进文档 ? 不错! 阅读指南 捐款。 。 。 .

如果你想帮助我们 维持我们的社区和项目,考虑 成为开放集体的出资人

见 支助CASL 详情

许可证

MIT 许可证

版本与发布记录

@casl/mongoose 共发布 51 个版本,首次发布于 2018/02/23;过去 12 个月发布了 7 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
9.1.0latest2026/10/2811 天前
9.0.22026/08/093 个月前
9.0.12026/07/074 个月前
9.0.02026/05/215 个月前
9.0.0-rcnext2026/05/086 个月前
8.0.52026/01/189 个月前

其他发布通道:canary → 2.0.0-alpha.ade5be4a,next → 9.0.0-rc。

关于 @casl/mongoose 的常见问题

@casl/mongoose 是什么?

允许根据 CASL 规则向 MongoDB 查询可访问的记录

如何安装 @casl/mongoose?

在项目目录运行 npm install @casl/mongoose,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:mongoose(^6.0.13 || ^7.0.0 || ^8.0.0 || ^9.0.0)、@casl/ability(^7.0.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@casl/mongoose 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@casl/mongoose 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@casl/mongoose 可以商用吗?

@casl/mongoose 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@casl/mongoose 的下载量有多少?

根据 npm 官方下载统计,@casl/mongoose 在 2026/10/09 – 2026/11/07 这 30 天内被下载 219,349 次,日均约 7,312 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@casl/mongoose 还在维护吗?

最新版本 9.1.0 发布于 2026/10/28(11 天前);至今共发布 51 个版本,其中过去 12 个月发布了 7 个;首次发布于 2018/02/23。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。