只分配 read 角色即可实现只允许聚合、禁止写入的效果,因聚合操作只需 read 权限,而 $out 和 $merge 等写阶段会因缺少目标库 write 权限报错,验证权限生效。

直接给 read 角色,别碰 write 相关权限
MongoDB 没有“只允许聚合、禁止写入”的内置角色,但你可以用标准角色组合实现这个效果。核心逻辑是:聚合操作($lookup、$group、$match 等)只要用户有对应集合的 read 权限就能执行;而所有写操作(insertOne、updateMany、deleteOne、drop 等)都需要显式授予 readWrite 或更高权限。所以最稳妥的做法就是只分配 read 角色。
- 在目标数据库(比如
analytics)中创建用户时,roles 字段只填{ role: "read", db: "analytics" } - 不要混用
readWrite、dbAdmin、userAdmin等任何带写能力的角色 - 如果用户需要跨库聚合(比如
$lookup查其他库),必须为每个被引用的库单独赋予read角色,例如:{ role: "read", db: "orders" }
注意 $out 和 $merge 会绕过 read 权限
聚合管道里有两个阶段是写操作:$out 和 $merge。它们不检查用户是否有 readWrite 角色,而是检查目标集合所在数据库的写权限 —— 如果你只给了 read,这两个阶段会直接报错 not authorized on 。这是好事,说明权限生效了。
-
$out会替换整个目标集合,要求对目标库有readWrite权限 -
$merge同样需要对目标集合所在库有readWrite(或更细粒度的insert/update权限) - 如果你发现用户居然能成功执行
$out,说明他实际被授予了目标库的readWrite或更高角色,得立刻检查db.getUsers()输出
自定义角色不是必须,但能防误配
用内置 read 角色足够安全,但如果你团队角色管理混乱,或者想显式禁掉某些读操作(比如禁止 collStats 或 listCollections),可以建一个最小化自定义角色:
db.createRole({
role: "readonly_aggregate",
privileges: [{
resource: { db: "analytics", collection: "events" },
actions: ["find", "collStats"]
}],
roles: []
})
- 这样连
show collections都会被拒绝(因为没给listCollections),比read更收敛 - 但要注意:
collStats是explain和部分聚合性能诊断必需的,禁了会影响排障 - 自定义角色必须在
admin库创建,且用户认证库仍得是admin(除非你把它 scope 到具体业务库)
容易被忽略的漏洞点:认证库和连接参数
即使角色配对了,用户仍可能因连接方式绕过限制。常见漏点:
- 用
mongosh --authenticationDatabase admin连,但角色只在analytics库,结果连上后默认进test库 —— 此时db.events.aggregate(...)会失败,因为没在正确库上下文 - 应用代码里硬编码了
authSource=admin,但用户实际在analytics库创建,导致认证失败或降级到匿名访问(如果服务端没开security.authorization) - Compass 或 Robo 3T 连接时没填
Authentication Database字段,连上了却看不到集合 —— 其实是权限生效了,只是 UI 没提示
真正起作用的是角色所绑定的数据库 + 用户登录时指定的认证库一致,而不是连接字符串里随便写的 authSource。











