内置read角色仅支持数据库级只读,无法限制到单个集合;必须用createrole()自定义角色,严格指定{db:"xxx",collection:"yyy"},collection为空或缺失即等同全库授权。

不能靠内置 read 角色实现集合级只读,必须用 createRole() 自定义角色,并严格指定 { db: "xxx", collection: "yyy" } ——漏掉 collection 字段或写成空字符串,就等于开放整个数据库。
为什么 read 角色做不到只读单个集合
内置 read 角色的权限 scope 是数据库级别,resource 定义里只有 db,没有 collection 字段。哪怕你在 myapp 库执行 db.createUser({ roles: ["read"] }),该用户也能读 myapp.users、myapp.logs、myapp.config 所有集合。MongoDB 不会自动把“只读库”理解成“只读其中某个集合”。
常见误操作包括:
- 在
admin库执行createUser()却期望权限生效于myapp库 ——read角色不跨库,必须切换到目标库再建 - 用
readAnyDatabase然后靠应用层过滤 —— 这属于权限失控,不是控制 - 以为加了
db参数就能限制集合 ——db只决定角色归属库,不缩小 resource 范围
用 createRole() 定义真正集合级只读角色
角色必须在 admin 数据库中创建(否则无法跨库授予),且 resource 必须同时包含 db 和 collection,二者缺一不可。例如,只允许读 sales 库的 orders 集合:
use admin
db.createRole({
role: "ordersReader",
privileges: [{
resource: { db: "sales", collection: "orders" },
actions: ["find"]
}],
roles: []
})
关键点:
-
collection: "orders"不能写成collection: ""或省略 —— 空值等同于通配,权限升格为全库 - 动作列表别偷懒写
["*"],只放明确需要的,比如只读就只写["find"] -
roles: []表示不继承其他角色,避免隐式权限叠加 - 如果还要支持聚合查询,得额外加上
"aggregate"到actions中(find权限本身不包含aggregate)
给用户绑定集合级角色时的 db 字段陷阱
用户可以在任意数据库下创建,但授予角色时的 db 字段,指的是「该角色定义所在的数据库」,不是用户认证库,也不是目标数据所在库。由于角色是在 admin 库创建的,所以授予时必须写 db: "admin":
use sales
db.createUser({
user: "reporter",
pwd: "xxx",
roles: [{ role: "ordersReader", db: "admin" }]
})
错误写法:
-
roles: [{ role: "ordersReader", db: "sales" }]—— MongoDB 找不到这个角色,报RoleNotFound -
roles: ["ordersReader"](没带db)—— 默认去当前库(sales)找角色,找不到 - 在
sales库执行createRole()—— 角色创建成功,但后续无法被其他库用户使用,失去复用性
验证权限是否真生效,别跳过这步
配置完立刻换账号测试,不要依赖“应该可以”。用新用户连接后,直接试几条典型命令:
-
db.orders.find().limit(1)—— 应成功 -
db.users.find().limit(1)—— 应报not authorized on sales to execute command { find: \"users\" ... } -
db.orders.aggregate([{$match: {}}])—— 若没在actions里加"aggregate",会失败
注意大小写敏感:collection: "Orders" 和实际集合名 orders 不匹配,权限就失效;连接池可能缓存旧权限,测试前最好重启客户端或显式 db.logout() 再重连。
最易被忽略的是:角色创建后,如果后续修改了 privileges,必须调用 grantPrivilegesToRole() 或 revokePrivilegesFromRole() 更新,直接改文档不生效;另外,createCollection 权限绝不能单独赋予集合级只读角色 —— 用户能建集合却读不了,容易触发误操作和监控盲区。











