必须在目标数据库执行db.createUser(),因为read角色作用域限于当前库,用户认证库由db参数决定,跨库无效;若在admin库创建则需显式指定db字段,否则权限失效。

只能在目标数据库上创建用户,并显式授予 read 角色,不能跨库授权,也不能用 readAnyDatabase。
为什么 db.createUser() 必须在目标库下执行
MongoDB 的用户是绑定到具体数据库(authenticationDatabase)的,角色权限也以该库为作用域起点。你在 admin 库运行 db.createUser() 并指定 { roles: [{ role: "read", db: "myapp" }] },这个用户仍属于 admin 库——但权限只对 myapp 有效。可一旦切换到其他库(如 test),没对应角色就查不了。更关键的是:如果误在 admin 库创建用户却漏写 db 字段,或用了 readAnyDatabase,就彻底失控。
- 正确做法:先
use myapp,再运行db.createUser() - 错误做法:在
admin库执行时忘记设db: "myapp",结果用户被建在admin下且无任何有效权限 - 验证方式:登录后执行
db.runCommand({ connectionStatus: 1 }).authInfo.authenticatedUserRoles,确认返回的角色里db字段确实是目标库名
read 角色在分片集群和副本集中的行为差异
read 是内置角色,但它的实际能力取决于部署架构。在副本集里它能读主从任意节点(只要连接的是可用节点);但在分片集群中,它只允许读取该库所在分片的数据——如果集合未分片,查询走主分片;如果已分片,mongos 会自动路由,但用户无需额外权限。不过要注意:如果应用直连某个 shard 节点(绕过 mongos),read 角色依然生效,但可能因数据不完整导致查询结果缺失。
- 分片环境下,确保应用通过
mongos连接,而非直连shard或config服务器 - 不要给只读用户
clusterMonitor等元数据角色,否则可通过listDatabases看到其他库名(虽不能读,但属信息泄露) - 若需聚合跨集合(同库内),
read已足够;跨库聚合则必须换角色,这违背“仅对特定库生效”前提
常见权限失效场景与排查步骤
用户创建后连得上却报 not authorized on myapp to execute command,大概率是认证库、连接字符串或角色绑定出问题。
- 检查连接字符串是否含
authSource:例如mongodb://ro_user:pwd@host:27017/myapp?authSource=myapp——authSource必须和用户所在库一致 - 确认驱动连接时指定的默认数据库就是目标库(比如 Node.js 的
mongoose.connect(url, { dbName: 'myapp' })),否则即使认证成功,后续操作也会因上下文库不匹配而被拒 - 用
db.getUser("ro_user")查看返回对象,重点核对roles[].db和roles[].role是否为"myapp"和"read" - 如果启用了
SCRAM-SHA-256认证(4.0+ 默认),旧客户端可能不支持,会静默失败——换驱动或降级认证机制(不推荐)
最容易被忽略的是 authSource 参数的位置和值,它不像 SQL 的 database 那样只是默认上下文,而是真实决定 MongoDB 去哪个库查用户凭证。少一个字母,权限就归零。











