一、MongoDB RBAC 概述
1. 什么是 RBAC?MongoDB 为什么选择 RBAC?
MongoDB 的 RBAC 体系提供了从粗粒度(数据库级)到细粒度(集合级、操作级)的完整权限控制能力。在生产环境中,我们应始终遵循以下核心原则:
- 最小权限:只给必要的,不多一分
- 角色复用:通过角色抽象权限,避免重复配置
- 定期审计:权限不是一次配置就结束的,需要持续审查
- 分层管理:应用账号、运维账号、管理账号严格分层
- 审计留痕:所有权限变更可追溯,满足合规要求
基于角色的访问控制(Role-Based Access Control, RBAC)是一种将权限授予角色、再将角色授予用户的访问控制模型。MongoDB 自 2.6 版本起完整引入 RBAC 体系,并在后续版本中持续增强。
MongoDB 选择 RBAC 而非 ACL(访问控制列表)或 MAC(强制访问控制),核心原因有三:
- 可扩展性:在大型系统中,为每个用户单独配置权限(ACL 模式)会导致管理开销呈指数级增长。RBAC 通过"角色"这一中间层实现权限的复用与抽象。
- 运维友好:角色可以在不同用户间重复使用,权限变更只需修改角色定义,无需逐个调整用户。
- 最小权限原则的自然承载:RBAC 的角色粒度可精细到单个集合的单个操作,为实现最小权限原则提供了技术基础。
2. 核心概念模型
MongoDB 授权模型由五个核心概念构成:
| 概念 | 定义 | 示例 |
|---|---|---|
| 用户(User) | 访问 MongoDB 的实体,在特定数据库中创建 | app_user@orders |
| 角色(Role) | 权限的命名集合,在特定数据库中定义 | readWrite、order_processor |
| 权限(Privilege) | 资源 + 操作的组合,是授权的最小单元 | 对 orders 库的 find 操作 |
| 资源(Resource) | 权限作用的目标,可以是集群、数据库、集合等 | orders.inventory 集合 |
| 操作(Action) | 允许在资源上执行的动作 | find、insert、createIndex |
一个关键理解 :用户 ≠ 数据库。用户虽然在某个数据库中创建(称为"认证数据库"),但通过角色可以获得对一个或多个数据库的操作权限。这意味着,一个在 orders 数据库中创建的用户,可以被授予对 products 数据库的只读权限------用户与数据库是解耦的。
3. 启用访问控制
MongoDB 默认不启用访问控制。在生产环境中,必须在启动时显式开启:
方式一:命令行参数
bash
mongod --auth --port 27017 --dbpath /data/db
方式二:配置文件(推荐)
yaml
security:
authorization: enabled
启用访问控制后,所有客户端连接都必须通过身份验证。需要注意:启用 authorization 的同时也会启用内部身份验证(如副本集成员间的认证)。
4. 认证→授权→执行 完整流程
下图展示了从客户端连接到操作执行的完整权限校验链路:

关键理解 :MongoDB 的角色只授予权限,从不限制权限。如果用户拥有两个角色,具有较高访问权限的角色优先------角色之间不存在"取交集"或"权限抵消"的逻辑。例如,即使同时拥有 read 和 readWriteAnyDatabase,read 也不会撤销对任何数据库的写入权限。这一设计意味着:撤销权限必须通过显式 revokeRolesFromUser 完成。
二、内置角色(Built-in Roles)解析
MongoDB 提供了一系列内置角色,覆盖了数据库系统中常见的权限需求。理解这些角色是设计权限体系的基础。
1. 数据库用户角色
| 角色 | 适用范围 | 核心能力 | 典型使用场景 |
|---|---|---|---|
read |
单个数据库 | 在所有非系统集合和 system.js 集合上执行读取操作 |
报表系统、数据分析只读账号 |
readWrite |
单个数据库 | read + 写入能力(insert/update/delete) |
应用程序的标准操作账号 |
read 角色不提供对 system.namespaces 等系统集合的直接访问。
2. 数据库管理角色
| 角色 | 适用范围 | 核心能力 | 典型使用场景 |
|---|---|---|---|
dbAdmin |
单个数据库 | Schema 相关操作:创建索引、查看 system.profile 等 |
数据库日常维护 |
userAdmin |
单个数据库 | 在当前数据库上创建和修改角色和用户 | 数据库级用户管理员 |
dbOwner |
单个数据库 | 组合了 readWrite + dbAdmin + userAdmin 的全部权限 |
数据库负责人 |
特别注意 :userAdmin 允许用户向任何用户(包括自己)授予任何权限,因此间接提供了对数据库的超级用户访问权限。使用此角色需格外谨慎。
3. 集群管理角色(仅限 admin 数据库)
| 角色 | 核心能力 |
|---|---|
clusterAdmin |
组合了 clusterManager + clusterMonitor + hostManager 的全部权限 |
clusterManager |
分片和副本集的管理操作(添加/删除节点、分片等) |
clusterMonitor |
监控工具使用的只读权限,可监控分片和副本集集群 |
hostManager |
服务器状态和操作日志管理 |
4. 备份与恢复角色(仅限 admin 数据库)
| 角色 | 核心能力 |
|---|---|
backup |
执行备份操作所需的权限(mongodump 等) |
restore |
执行恢复操作所需的权限(mongorestore 等) |
5. 跨数据库角色(仅限 admin 数据库)
| 角色 | 核心能力 |
|---|---|
readAnyDatabase |
对所有数据库的只读权限 |
readWriteAnyDatabase |
对所有数据库的读写权限 |
userAdminAnyDatabase |
在所有数据库上管理用户和角色的权限 |
dbAdminAnyDatabase |
对所有数据库的管理权限 |
6. 超级用户角色
| 角色 | 核心能力 |
|---|---|
root |
组合了 readWriteAnyDatabase + dbAdminAnyDatabase + userAdminAnyDatabase + clusterAdmin + backup + restore 等所有关键角色的权限 |
root 可执行任何操作------创建/删除数据库、管理用户、集群配置、备份恢复等。严禁在日常操作中使用。
7. 内置角色速查表
| 类别 | 角色名称 | 数据库范围 | 权限级别 |
|---|---|---|---|
| 数据库用户 | read |
单库 | ★★☆ |
| 数据库用户 | readWrite |
单库 | ★★★ |
| 数据库管理 | dbAdmin |
单库 | ★★★☆ |
| 数据库管理 | userAdmin |
单库 | ★★★★ |
| 数据库管理 | dbOwner |
单库 | ★★★★ |
| 跨库 | readAnyDatabase |
全局 | ★★★☆ |
| 跨库 | readWriteAnyDatabase |
全局 | ★★★★ |
| 跨库 | userAdminAnyDatabase |
全局 | ★★★★★ |
| 集群 | clusterAdmin |
全局 | ★★★★★ |
| 备份恢复 | backup / restore |
全局 | ★★★★ |
| 超级用户 | root |
全局 | ★★★★★ |
三、自定义角色与集合级访问控制
内置角色虽覆盖了常见场景,但生产环境中的权限需求往往更加精细。当内置角色无法满足需求时,必须创建自定义角色。
1. 何时需要自定义角色?
- 集合级权限隔离 :同一数据库下不同集合需要独立的读写权限时。例如,订单库中
order_main仅读、order_log可写。 - 操作级精细化 :只允许
find和insert,但不允许update和delete。 - 业务语义控制 :内置角色只管理"能不能执行
find",不管"能不能删user:123的订单"------这类业务权限必须在应用层建模。 - 多业务线隔离:每个业务线专属数据库配最小权限角色,禁用粗粒度的内置角色。
2. 创建自定义角色的完整语法
使用 db.createRole() 方法创建自定义角色:
javascript
db.createRole({
role: "<角色名称>",
privileges: [
{
resource: { <资源定义> },
actions: [ "<操作1>", "<操作2>", ... ]
}
],
roles: [
{ role: "<继承角色>", db: "<数据库>" }
],
authenticationRestrictions: [
{
clientSource: ["<IP地址>", "<CIDR范围>"],
serverAddress: ["<IP地址>", "<CIDR范围>"]
}
]
})
参数说明:
| 字段 | 类型 | 说明 |
|---|---|---|
role |
字符串 | 新角色的名称 |
privileges |
数组 | 授予角色的权限,由资源(resource)和操作(actions)组成 |
roles |
数组 | 该角色继承权限的源角色列表 |
authenticationRestrictions |
数组 | 可选,IP 地址或 CIDR 范围限制 |
前置条件 :创建角色需要拥有 userAdmin 或 userAdminAnyDatabase 角色。
3. 集合级访问控制实战:电商订单系统权限设计
以电商系统为例,我们为订单数据库设计一套精细的权限矩阵:
业务需求:
- 订单主表
orders.order_main:客服团队只读 - 订单日志表
orders.order_log:审计团队可读写 - 退款表
orders.refund:财务团队可读写,其他团队不可见 - 不允许任何非管理员账号删除数据
创建自定义角色:
javascript
// 切换到 orders 数据库
use orders
// 1. 客服只读角色 - 仅限 order_main 集合
db.createRole({
role: "customer_service_read",
privileges: [
{
resource: { db: "orders", collection: "order_main" },
actions: [ "find", "listCollections" ]
}
],
roles: []
})
// 2. 审计读写角色 - 仅限 order_log 集合
db.createRole({
role: "audit_read_write",
privileges: [
{
resource: { db: "orders", collection: "order_log" },
actions: [ "find", "insert", "update" ]
}
],
roles: []
})
// 3. 财务角色 - refund 集合完全控制,但无删除权限
db.createRole({
role: "finance_operator",
privileges: [
{
resource: { db: "orders", collection: "refund" },
actions: [ "find", "insert", "update", "listCollections" ]
}
// 注意:没有包含 "delete" 或 "dropCollection"
],
roles: []
})
// 4. 订单管理员 - 继承 readWrite,但额外限制不能删除
// 注意:由于 readWrite 包含删除权限,此处无法通过继承"取消"权限
// 正确的做法是显式列出所需操作,而非继承粗粒度角色
db.createRole({
role: "order_manager",
privileges: [
{
resource: { db: "orders", collection: "order_main" },
actions: [ "find", "insert", "update", "listCollections" ]
},
{
resource: { db: "orders", collection: "order_log" },
actions: [ "find", "insert", "update" ]
}
],
roles: []
})
分配用户:
javascript
use orders
db.createUser({
user: "cs_zhang",
pwd: "<安全密码>",
roles: [
{ role: "customer_service_read", db: "orders" }
]
})
4. 自定义角色的作用域与继承规则
理解自定义角色的作用域是权限设计的核心:
| 角色创建位置 | 可授予的权限范围 | 可继承的角色范围 |
|---|---|---|
| 非 admin 数据库 | 仅限该数据库的资源 | 仅限同一数据库中的其他角色 |
| admin 数据库 | admin 数据库、其他任何数据库、集群资源 | 任何数据库中的任何角色 |
核心要点:
- MongoDB 使用"数据库名 + 角色名"的组合来唯一定义角色
- 所有角色信息统一存储在
admin.system.roles集合中 - 不要直接操作
system.roles集合,应使用角色管理命令
5. 自定义角色创建流程

四、最小权限原则的生产实践
最小权限原则(Principle of Least Privilege)要求:仅授予用户完成任务所需的最小权限,不多一分。在 MongoDB 生产环境中,这是保障数据安全的第一道防线。
1. 为什么最小权限在 MongoDB 中尤为重要?
- 数据泄露的放大器:一个过度授权的应用账号被攻破,攻击者即可获得远超预期的数据访问能力
- 运维事故的催化剂 :拥有
dropDatabase权限的账号,一次误操作即可造成灾难性后果 - 合规审计的硬要求:等保、GDPR、SOC2 等标准均明确要求实施最小权限
2. 过度授权 vs. 最小权限------风险对照
| 场景 | 过度授权的做法 | 风险 | 最小权限的正确做法 |
|---|---|---|---|
| 报表应用 | 授予 readWriteAnyDatabase |
可读写所有数据库,一旦被攻击全库沦陷 | 授予 read 仅限目标数据库 |
| 运维脚本 | 使用 root 角色 |
误删数据库无法挽回 | 创建仅包含所需操作的自定义角色 |
| 开发环境 | 所有开发人员共享 dbOwner |
权限边界模糊,难以追责 | 每人独立账号,按需授权 |
| 备份账号 | 授予 root 执行备份 |
备份介质泄露即全库暴露 | 授予 backup 角色 |
3. 生产环境角色设计方法论
分层角色设计 :

实践要点:
- 仅在 admin 库创建高权限账户,应用账户严格限定在目标业务库
- 为每个应用/人员创建唯一的 MongoDB 用户
- 创建角色而非直接分配权限,实现权限的复用与统一管理
- 避免在应用中使用
root角色进行日常操作 - 定期审查权限 :使用
db.getUsers()和db.getRoles({showPrivileges: true})进行审计
4. 生产环境用户创建的标准操作流程
javascript
// 1. 首先创建管理员用户(第一个用户必须具有 userAdmin 权限)
use admin
db.createUser({
user: "admin",
pwd: "<强密码>",
roles: [ { role: "userAdminAnyDatabase", db: "admin" } ]
})
// 2. 使用管理员账号认证后,创建应用专用用户
use orders
db.createUser({
user: "order_app",
pwd: "<强密码>",
roles: [
{ role: "readWrite", db: "orders" } // 仅限 orders 库
// 不要授予其他数据库的任何权限
]
})
// 3. 创建只读报表用户
use orders
db.createUser({
user: "report_reader",
pwd: "<强密码>",
roles: [
{ role: "read", db: "orders" }
]
})
// 4. 为 DBA 创建独立的精细化管理账号
use admin
db.createUser({
user: "dba_zhang",
pwd: "<强密码>",
roles: [
{ role: "dbAdminAnyDatabase", db: "admin" },
{ role: "clusterMonitor", db: "admin" }
// 注意:没有 userAdmin,避免权限过度集中
]
})
5. 分片集群的权限管理特殊注意事项
根据用户对分片集群的架构选择(选项1C:副本集为主,补充分片集群注意事项),以下补充分片集群的关键差异:
分片集群的用户创建位置:
- 常规操作为:连接到
mongos实例,在mongos上创建用户 - 用户信息存储在 config 服务器的 admin 数据库中
- 如需创建仅针对特定分片的本地用户,需直接登录该分片的 primary 节点
分片集群的组件间认证:
- 分片集群各组件(
mongos、mongod、config服务器)之间需要使用 KeyFile 进行内部身份认证 - 在生产环境中,正确的顺序是:先在本地创建
root@admin→ 开启authorization→ 按库分权
分片集群的特殊角色:
directShardOperations角色:允许自动化代理执行需要直接访问分片的维护操作(如添加分片),仅分片集群需要
五、审计与合规------满足等保要求的实践指南
1. MongoDB 审计日志概述
MongoDB 的审计日志可完整记录所有管理操作及数据变更。等保三级要求保留 6 个月以上的审计日志,且日志需满足"不可篡改"要求。
审计日志包含的关键事件类型:
- 认证事件:登录成功/失败
- 授权操作:角色变更、权限授予/撤销
- 数据库操作:CRUD 操作
2. 审计日志配置
yaml
# mongod.conf
auditLog:
destination: file
format: JSON
path: /var/log/mongodb/audit.log
filter: '{ "atype": { "$in": ["authCheck", "createUser", "dropUser", "grantRoles", "revokeRoles"] } }'
配置要点:
- 通过
auditLog.destination指定输出目标(文件/syslog/控制台) - 设置
auditLog.format为JSON以便解析和归档 - 使用
filter过滤审计事件,避免日志过度膨胀
3. 权限审计的常用命令
javascript
// 查看所有用户及其角色
db.getUsers()
// 查看角色的详细权限(含继承展开)
db.getRoles({ rolesInfo: 1, showPrivileges: true, showBuiltinRoles: true })
// 查看特定用户的权限详情
db.getUser("order_app", { showPrivileges: true })
// 审计日志分析(示例:查询最近的角色变更操作)
// 需配合日志分析工具(如 ELK)对 JSON 格式的审计日志进行检索
实践建议:
- 建立每周权限审查机制,导出所有用户权限清单
- 对高权限角色(
root、userAdminAnyDatabase、clusterAdmin)的使用进行月度专项审计 - 所有权限变更必须关联工单号,确保可追溯
六、常见面试题
Q1:MongoDB 默认是否启用访问控制?如何启用?
参考答案 :
MongoDB 默认不启用访问控制。需要在启动时通过以下方式之一启用:
- 命令行参数:
mongod --auth - 配置文件:在
mongod.conf中设置security.authorization: enabled
启用后,所有客户端连接都必须通过身份验证。第一个用户必须在启用前创建(或通过 localhost 豁免机制在启用后创建)。
Q2:read 和 readWrite 角色的区别是什么?dbOwner 包含了哪些权限?
参考答案:
read:仅提供读取 权限(find、listCollections等),适用于只读报表账号readWrite:提供read的全部权限 + 写入 权限(insert、update、delete)
dbOwner 是数据库所有者的角色,组合了 readWrite + dbAdmin + userAdmin 三个角色的全部权限。它是一个高权限角色,应谨慎使用。
Q3:如何创建一个只能对 products.inventory 集合执行 find 和 insert 操作的自定义角色?
参考答案:
javascript
use products
db.createRole({
role: "inventory_operator",
privileges: [
{
resource: { db: "products", collection: "inventory" },
actions: [ "find", "insert" ]
}
],
roles: [] // 不继承任何角色,确保权限最小化
})
关键点:
- 使用
resource精确定位到products.inventory集合 actions数组只包含find和insert,不包含update和deleteroles为空数组,避免继承不必要的权限
Q4:一个用户同时拥有 read(orders 库)和 readWriteAnyDatabase 两个角色,他对 orders 库的权限是什么?
参考答案 :
该用户对 orders 数据库拥有读写权限。
原因:MongoDB 的角色从不限制权限 。当用户拥有多个角色时,具有更高访问权限的角色优先。readWriteAnyDatabase 包含对所有数据库的读写权限,因此 read 角色不会撤销对 orders 库的写入能力。
重要推论 :不能通过授予一个低权限角色来"抵消"另一个高权限角色。撤销权限必须使用 revokeRolesFromUser。
Q5:在非 admin 数据库中创建的角色,能否继承 admin 数据库中角色的权限?
参考答案 :
不能。
规则如下:
- 在非 admin 数据库 中创建的角色:只能继承同一数据库中的其他角色
- 在 admin 数据库 中创建的角色:可以继承任何数据库中的角色
因此,如果需要在非 admin 库的角色中继承 admin 库的权限,必须将该角色创建在 admin 数据库中。
Q6:生产环境中,应用账号应该使用什么角色?为什么不能用 root?
参考答案 :
生产环境中的应用账号应使用最小必要权限的角色:
- 如果只需读取,使用
read限定到目标数据库 - 如果需要读写,使用
readWrite限定到目标数据库 - 如需更精细的控制,使用自定义角色精确到集合级别
禁止使用 root 的原因:
root拥有全库所有操作的权限,一旦应用被攻破,整个数据库集群沦陷- 运维事故风险极高:一次误操作(如
db.dropDatabase())即可造成灾难 - 不符合最小权限原则,无法通过合规审计
Q7:分片集群中,用户应该在哪里创建?用户信息存储在哪里?
参考答案:
- 常规情况下,应连接到
mongos实例创建用户 - 用户信息存储在 config 服务器的
admin数据库中 - 如果需要在特定分片 上创建本地用户(例如分片级别的维护账号),需直接登录该分片的 primary 节点,此时用户信息存储在该分片的
admin数据库中
分片集群的组件间还需使用 KeyFile 进行内部身份认证。