Skip to content

访问控制

MongoDB3.6.12 + centos7.9

MongoDB提供了各种功能,例如身份验证,访问控制,加密,以保护MongoDB的数据安全。

这里我们重点介绍基于角色的访问控制,也就是MongoDB中的用户管理。

MongoDB使用基于角色的访问控制(RBAC)来管理对MongoDB系统的访问。授予用户一个或多个角色,这些角色确定用户对数据库资源和操作的访问权限。

启用访问控制

MongoDB默认情况下不启用访问控制。但可以使用--authsecurity.authorization设置启用授权。

启用访问控制后,用户必须对自己进行身份验证

用户和角色

我们可以在创建用户时,同时指定角色,明确用户对于哪些资源可以进行操控。

我们也可以为现有用户授权或者撤销角色,后续会具体展开讲解。

内置角色和权限

MongoDB提供了内置角色,可提供数据库系统中通常所需的不同访问级别。

接下来简要介绍MongoDB中的内置角色和内置权限。

内置权限

PermissionDescription
root只在admin数据库中可用。拥有对MongoDB的所有权限
read允许用户读取指定数据库
readWrite允许用户读写指定数据库
dbAdmin允许用户在指定数据库中执行管理函数,如索引创建、删除,查看统计或访问system.profile
dbOwner允许用户对指定库的所有能力,包含readWrite、dbAdmin、userAdmin这些角色的权限
userAdmin允许用户向system.users集合写入,可以找指定数据库里创建、删除和管理用户
clusterAdmin只在admin数据库中可用,赋予用户所有分片和复制集相关函数的管理权限
readAnyDatabase只在admin数据库中可用,赋予用户所有数据库的读权限
readWriteAnyDatabase只在admin数据库中可用,赋予用户所有数据库的读写权限
userAdminAnyDatabase只在admin数据库中可用,赋予用户所有数据库的userAdmin权限
dbAdminAnyDatabase只在admin数据库中可用,赋予用户所有数据库的dbAdmin权限

内置角色

RoleDescription
超级用户角色root
数据库用户角色read、readWrite
数据库管理角色dbAdmin、dbOwner、userAdmin
集群管理角色clusterAdmin、clusterManager、clusterMonitor、hostManager
备份恢复角色backup、restore
所有数据库角色readAnyDatabase、readWriteAnyDatabase、userAdminAnyDatabase、dbAdminAnyDatabase

更多关于内置角色和权限参考官网:https://docs.mongodb.com/v3.6/core/security-built-in-roles/

注意,如果内置的角色无法满足一些特定的场景,MongoDB还允许我们自定义角色。

用户管理

我们通常先创建具有userAdmin或者userAdminAnyDatabase角色的admin用户后,再开启访问控制。

当开启访问控制后,MongoDB会强制执行访问用户的身份认证。

在展开讲解之前,需要了解一些概念。

认证库的概念

认证库(Authentication Database)也叫验证库,是MongoDB在用户安全方面的重要一部分,用户在登录时,必须加上认证库才能操作。

建议,请将认证库指定为使用的库。

用户管理的相关方法

NameDescription
db.auth()向数据库验证用户
db.changeUserPassword()更改现有用户的密码
db.createUser()创建一个新用户
db.dropUser()删除一个用户
db.dropAllUsers()删除与数据库关联的所有用户
db.getUser()返回有关指定用户的信息
db.getUsers()返回有关与数据库关联的所有用户的信息
db.grantRolesToUser()向用户授予角色及其特权
db.revokeRolesFromUser()从用户删除角色
db.updateUser()更新用户数据

详情参考官档:https://docs.mongodb.com/v3.6/reference/method/js-user-management/#user-management-methods

创建用户

创建用户语法参考:

bash
{
  user: "<name>",
  pwd: "<cleartext password>",
  customData: { <any information> },
  roles: [
    { role: "<role>", db: "<database>" },
    { role: "<role>", db: "<database>" },
    "role", "role",
  ],
  authenticationRestrictions: [
     {
       clientSource: ["<IP>" | "<CIDR range>", ...],
       serverAddress: ["<IP>" | "<CIDR range>", ...]
     },
     ...
  ]
}

其中:

  • user是用户名。
  • pwd是密码,且密码必须是字符串。
  • customData可以备注一些自定义的信息,比如说这个用户哪个员工在使用。
  • roles对应一个列表,列表套字典:
    • role是角色。
    • db是要从操作的库,同时也是认证库。
  • authenticationRestrictions限制身份,用的较少。

在创建用户时,我们通常先创建root超级用户,然后通过root用户来创建其他的用户。

bash
# 创建admin用户,赋予userAdminAnyDatabase角色对应的权限,用于管理所有用户信息
use admin
db.createUser({
	user: "admin",
	pwd: "1234",
	roles: [
		{ role: "userAdminAnyDatabase", db: "admin" }
  ]
})

# 创建超级管理员用户,拥有所有权限
use admin
db.createUser({
	user: "root",
	pwd: "1234",
	roles: [
		{ role: "root", db: "admin" }
  ]
})

# 创建普通用户, 认证库是 admin, 对 t1 库具有 read 角色对应的权限,同时具有 backup角色对应的权限
use admin
db.createUser({
	user: "zhangkai1",
	pwd: "1234",
	roles: [
		{ role: "read", db: "t1" },
        "backup"
  ]
})

# 创建普通用户, 认证库是 t1, 对 t1 库具有 readWrite 角色对应的权限,同时具有 backup角色对应的权限
use t1
db.createUser({
	user: "zhangkai2",
	pwd: "1234",
	roles: [
		{ role: "readWrite", db: "t1" },
        "backup"
  ]
})

# 创建普通用户, 认证库是 t1,尚未分配角色
use t1
db.createUser({
	user: "zhangkai3",
	pwd: "1234",
	roles: []
})

注意:

  • 创建用户时,必须先use到某个数据库下,而且该数据库将作为用户的认证库。
  • 创建管理员类的用户时,必须先use admin后再创建用户,且用户的认证库就是admin
  • 创建普通的用户时:
    • use admin后在创建用户,该用户的认证库就是admin。
    • use到一个普通数据库下创建的用户,该用户的认证库就是这个普通数据库。
  • 一般建议管理员类的用户认证库为admin,普通用户将认证库和要操作的数据库设置为同一个即可。
  • 当用户登录时,必须指定认证库。

另外要说明的是,由于root权限比admin大,操作起来也更便捷,后续的所有用户管理的操作,我都以root用户来操作。

开启认证

开启认证有两种方式。

第一种就是通过在启动时配置--auth参数开启用户认证:

bash
[mongod@cs ~]$ mongod -f /data/mongodb_data/27017/conf/mongodb.conf --fork --auth

第二种,则是直接在配置文件中进行配置:

bash
[mongod@cs ~]$ vim /data/mongodb_data/27017/conf/mongodb.conf 

# 安全配置相关
security:
  # 当开启该配置时,登录需要用户认证,如果你没有可用的用户,请勿配置该参数
  authorization: enabled  
  
[mongod@cs ~]$ systemctl restart mongod
==== AUTHENTICATING FOR org.freedesktop.systemd1.manage-units ===
Authentication is required to manage system services or units.
Authenticating as: root
Password: 
==== AUTHENTICATION COMPLETE ===

当开启认证之后,登录就要加认证了:

bash
[mongod@cs ~]$ mongo	# 无认证,可以登录,但啥也做不了
> use admin
switched to db admin
> show tables
2021-01-20T15:02:19.518+0800 E QUERY    [thread1] Error: listCollections failed: {
	"ok" : 0,
	"errmsg" : "there are no users authenticated",
	"code" : 13,
	"codeName" : "Unauthorized"
} :
_getErrorWithCode@src/mongo/shell/utils.js:25:13
DB.prototype._getCollectionInfosCommand@src/mongo/shell/db.js:941:1
DB.prototype.getCollectionInfos@src/mongo/shell/db.js:953:19
DB.prototype.getCollectionNames@src/mongo/shell/db.js:964:16
shellHelper.show@src/mongo/shell/utils.js:853:9
shellHelper@src/mongo/shell/utils.js:750:15
@(shellhelp2):1:1

# 此时,就需要通过一个内置方法来进行认证
> db.auth("root", "1234")
1
> show tables
system.users
system.version
> exit

不推荐上面的认证方式,通常在登录时加上认证:

bash
# 标准登录语法,当然,你不要在 -p 后直接跟密码  /admin 是认证库
[mongod@cs ~]$ mongo -uroot -p1234 127.0.0.1:27017/admin
# 如果是默认端口,可以省略不写
[mongod@cs ~]$ mongo -uroot -p1234 127.0.0.1/admin
# 如果是远程登录
[mongod@cs ~]$ mongo -uroot -p1234 10.0.0.200:27017/admin

认证成功,可以通过以下几个命令来查看用户了:

bash
# 所有的用户都存在admin库下的system.users集合中
> use admin
switched to db admin
> show tables
system.users
system.version

# 查看所有用户
> db.system.users.find()
> db.system.users.find().pretty()

# 查看指定用户
> db.system.users.find({"user":"root"})

# 查看跟当前所在库绑定的用户/所有用户
> show users
> db.getUser("root")
> db.getUsers()

更改用户

如果仅更改用户密码:

bash
# 首先use到对应的库
> use t1
switched to db t1

# 通过show命令查看当前库绑定的用户
> show users
{
	"_id" : "t1.zhangkai3",
	"user" : "zhangkai3",
	"db" : "t1",
	"roles" : [ ]
}
# db.changeUserPassword("要修改的用户名", "新的密码")
> db.changeUserPassword("zhangkai3", "abc")
> exit
bye
[mongod@cs ~]$ mongo -uzhangkai3 -pabc 127.0.0.1/t1

更改用户其他信息:

bash
> use t1
switched to db t1
> show users
{
	"_id" : "t1.zhangkai3",
	"user" : "zhangkai3",
	"db" : "t1",
	"roles" : [ ]
}
> db.updateUser("zhangkai3",{
	customData: {"info": "除了用户名不能更改,其他的都可以更改"},
	pwd: "1234",
	roles: [
		{ role: "read", db: "t1" }
  ]
})
> show users
{
	"_id" : "t1.zhangkai3",
	"user" : "zhangkai3",
	"db" : "t1",
	"roles" : [
		{
			"role" : "read",
			"db" : "t1"
		}
	],
	"customData" : {
		"info" : "除了用户名不能更改,其他的都可以更改"
	}
}

注意,db.updateUser方法中, roles参数用来更新用户的角色信息,比如为一个无角色用户添加角色,更改角色都用该参数指定。

删除用户

删除用户有以下几种方式:

bash
# 首先 db.removeUser() 在MongoDB2.6版本开始被弃用,

# 使用 db.dropUser() 删除用户
# 删除当前库下的用户
[mongod@cs ~]$ mongo -uroot -p1234 127.0.0.1/admin
> use t1
> show users
{
	"_id" : "t1.zhangkai3",
	"user" : "zhangkai3",
	"db" : "t1",
	"roles" : [ ]
}
{
	"_id" : "t1.zhangkai4",
	"user" : "zhangkai4",
	"db" : "t1",
	"roles" : [ ]
}
> db.dropUser("zhangkai4")
true
> show users
{
	"_id" : "t1.zhangkai3",
	"user" : "zhangkai3",
	"db" : "t1",
	"roles" : [ ]
}


# 也可以从 system.users 集合中删除
# 必须先use到admin库中才能删除
> use admin
> db.system.users.deleteOne({"user": "zhangkai5"})


# 删除当前库下所有用户
# 先创建两个用户
> use t2
switched to db t2
> db.createUser({user: "zhangkai5", pwd: "1234", roles: []})
Successfully added user: { "user" : "zhangkai5", "roles" : [ ] }
> db.createUser({user: "zhangkai6", pwd: "1234", roles: []})
Successfully added user: { "user" : "zhangkai6", "roles" : [ ] }
> show users
{
	"_id" : "t2.zhangkai5",
	"user" : "zhangkai5",
	"db" : "t2",
	"roles" : [ ]
}
{
	"_id" : "t2.zhangkai6",
	"user" : "zhangkai6",
	"db" : "t2",
	"roles" : [ ]
}
# 删除当前库绑定的所有用户
> db.dropAllUsers()
NumberLong(2)			# 结果提示有两个用户被删除

角色管理

角色管理的相关方法

NameDescription
db.createRole()创建一个角色并指定其特权
db.dropRole()删除用户定义的角色
db.dropAllRoles()删除与数据库关联的所有用户定义角色
db.getRole()返回指定角色的信息
db.getRoles()返回数据库中所有用户定义角色的信息
db.grantPrivilegesToRole()将特权分配给用户定义的角色
db.revokePrivilegesFromRole()从用户定义的角色中删除指定的特权
db.grantRolesToRole()指定角色,用户定义的角色将从这些角色继承特权
db.revokeRolesFromRole()从角色中删除继承的角色
db.updateRole()更新用户定义的角色

详情参考官档:https://docs.mongodb.com/v3.6/reference/method/js-role-management/#role-management-methods

查看角色

查看角色有两个方法:

bash
db.getRoles()   # 查看当前库下所有的自定义角色
db.getRole("read")   # 查看指定内置或者自定义角色

默认的, MongoDB为每个可用的数据库,绑定所有内置的角色:

bash
[mongod@cs ~]$ mongo -uroot -p1234 127.0.0.1/admin
> use t3
switched to db t3
> db.getUsers()   # 此时 t3 还没有绑定用户
[ ]
> db.getRoles()   # 查看当前库下所有的自定义角色
[ ]
> db.getRole("read")   # 查看所有内置或者自定义角色
{
	"role" : "read",
	"db" : "t3",
	"isBuiltin" : true,  # 可以发现read角色时内置角色
	"roles" : [ ],
	"inheritedRoles" : [ ]
}

创建和添加角色

当内置角色不能满足特定场景时,可以通过db.createRole方法来创建自定义角色,相关语法:

bash
# 语法参考
{
  role: "<name>",
  privileges: [
     { resource: { <resource> }, actions: [ "<action>", ... ] },
     ...
  ],
  roles: [
     { role: "<role>", db: "<database>" } | "<role>",
      ...
  ],
  authenticationRestrictions: [
    {
      clientSource: ["<IP>" | "<CIDR range>", ...],
      serverAddress: ["<IP>" | "<CIDR range>", ...]
    },
    ...
  ]
}
      
# 示例参考
use admin
db.createRole(
   {
     role: "myClusterwideAdmin",  # 自定义角色名字
     privileges: [  # 拥有哪些特权
       { resource: { cluster: true }, actions: [ "addShard" ] },
       { resource: { db: "config", collection: "" }, actions: [ "find", "update", "insert", "remove" ] },
       { resource: { db: "users", collection: "usersCollection" }, actions: [ "update", "insert", "remove" ] },
       { resource: { db: "", collection: "" }, actions: [ "find" ] }
     ],
     roles: [  # 对 admin 库有 read 权限
       { role: "read", db: "admin" }
     ]
   },
   { w: "majority" , wtimeout: 5000 }  # 写确认和写超时
)

来个示例:

bash
[mongod@cs ~]$ mongo -uroot -p1234 127.0.0.1/admin
# 创建一个无角色用户
> use t3
> db.createUser({user: "zhangkai6", pwd: "1234", roles: []})
> db.getUser("zhangkai6")
{
	"_id" : "t3.zhangkai6",
	"user" : "zhangkai6",
	"db" : "t3",
	"roles" : [ ]
}

# 创建一个自定义的角色 myRole1,拥有的特权:可以对 t3 库下所有集合进行 "find", "update", "insert", "remove" 操作
# 拥有对 t3 的 readWrite 权限
> db.createRole(
   {
     role: "myRole1",
     privileges: [
       { resource: { db: "t3", collection: "" }, actions: [ "find", "update", "insert", "remove" ] }
     ],
     roles: [
       { role: "readWrite", db: "t3" }
     ]
   }
)
> db.getRole("myRole1")
{
	"role" : "myRole1",
	"db" : "t3",
	"isBuiltin" : false,
	"roles" : [
		{
			"role" : "readWrite",
			"db" : "t3"
		}
	],
	"inheritedRoles" : [
		{
			"role" : "readWrite",
			"db" : "t3"
		}
	]
}


# 将自定义角色赋予用户
> db.updateUser("zhangkai6", {"roles": [{"role": "myRole1", "db": "t3"}]})
> db.getUser("zhangkai6")
{
	"_id" : "t3.zhangkai6",
	"user" : "zhangkai6",
	"db" : "t3",
	"roles" : [
		{
			"role" : "myRole1",
			"db" : "t3"
		}
	]
}

更改角色

为现有的自定义角色授予新的特权

bash
# 先创建一个自定义角色
db.createRole(
   {
     role: "myRole2",
     privileges: [
       { resource: { db: "t3", collection: "" }, actions: [ "find" ] }
     ],
     roles: [
       { role: "readWrite", db: "t3" }
     ]
   }
)

# 此时该自定义角色的 privileges 是这样的
> db.getRole("myRole2", {"showPrivileges":true})["privileges"]
[
	{
		"resource" : {
			"db" : "t3",
			"collection" : ""
		},
		"actions" : [
			"find"
		]
	}
]

# 通过下面方法添加新特权
> db.grantPrivilegesToRole(
	"myRole2",
	[
		{ resource: { db: "t3", collection: "" }, actions: ["update", "insert"] }
	]
)
> db.getRole("myRole2", {"showPrivileges":true})["privileges"]
[
	{
		"resource" : {
			"db" : "t3",
			"collection" : ""
		},
		"actions" : [
			"find",
			"insert",
			"update"
		]
	}
]

为现有自定义角色删除指定特权

bash
# 现在,自定义角色具有以下特权
> db.getRole("myRole2", {"showPrivileges":true})["privileges"]
[
	{
		"resource" : {
			"db" : "t3",
			"collection" : ""
		},
		"actions" : [
			"find",
			"insert",
			"update"
		]
	}
]

# 通过 删除 update 特权
> db.revokePrivilegesFromRole(
   "myRole2",
   [
     {resource : {db : "t3",collection : ""}, actions : ["update"]}
   ]
)
> db.getRole("myRole2", {"showPrivileges":true})["privileges"]
[
	{
		"resource" : {
			"db" : "t3",
			"collection" : ""
		},
		"actions" : [
			"find",
			"insert"
		]
	}
]

# 删除多个特权
> db.revokePrivilegesFromRole(
   "myRole2",
   [
     {resource : {db : "t3",collection : ""}, actions : ["update"]},
     {resource : {db : "t3",collection : ""}, actions : ["find", "insert"]}
   ]
)

将内置或自定义角色赋予自定义的角色

bash
# myRole1是现有的自定义角色,可以在列表中写多个自定义角色
> db.grantRolesToRole(
	"myRole1",
	["myRole2"]
)
> db.getRole("myRole1")
{
	"role" : "myRole1",
	"db" : "t3",
	"isBuiltin" : false,
	"roles" : [
		{
			"role" : "readWrite",
			"db" : "t3"
		},
		{
			"role" : "myRole2",
			"db" : "t3"
		}
	],
	"inheritedRoles" : [
		{
			"role" : "myRole2",
			"db" : "t3"
		},
		{
			"role" : "readWrite",
			"db" : "t3"
		}
	]
}

从现有自定义角色中删除内置或自定义角色

bash
# 现在,myRole1 角色有如下角色
> db.getRole("myRole1")["roles"]
[
	{
		"role" : "myRole2",
		"db" : "t3"
	},
	{
		"role" : "readWrite",
		"db" : "t3"
	}
]
> db.revokeRolesFromRole("myRole1", ["readWrite", "myRole2"])
> db.getRole("myRole1")["roles"]
[ ]

更新角色

bash
# 现在的 myRole2 的角色信息
> db.getRole("myRole2")
{
	"role" : "myRole2",
	"db" : "t3",
	"isBuiltin" : false,
	"roles" : [
		{
			"role" : "readWrite",
			"db" : "t3"
		}
	],
	"inheritedRoles" : [
		{
			"role" : "readWrite",
			"db" : "t3"
		}
	]
}

# db.updateRole 将完全覆盖指定自定义角色的所有角色信息,慎用
> db.updateRole(
	"myRole2",
   {
     privileges: [
       { resource: { db: "t3", collection: "" }, actions: ["find"] }
     ],
     roles: [
       { role: "read", db: "t3" }
     ]
   }
)
> db.getRole("myRole2")
{
	"role" : "myRole2",
	"db" : "t3",
	"isBuiltin" : false,
	"roles" : [
		{
			"role" : "read",
			"db" : "t3"
		}
	],
	"inheritedRoles" : [
		{
			"role" : "read",
			"db" : "t3"
		}
	]
}

删除角色

删除角色非常简单:

bash
# 现在 t3 下有两个自定义角色
> db
t3
> db.getRoles()
[
	{
		"role" : "myRole1",
		"db" : "t3",
		"isBuiltin" : false,
		"roles" : [ ],
		"inheritedRoles" : [ ]
	},
	{
		"role" : "myRole2",
		"db" : "t3",
		"isBuiltin" : false,
		"roles" : [
			{
				"role" : "read",
				"db" : "t3"
			}
		],
		"inheritedRoles" : [
			{
				"role" : "read",
				"db" : "t3"
			}
		]
	}
]


# 删除当前库下的指定自定义角色
> db.dropRole("myRole1")
true
> db.getRoles()  # 此时还剩一个自定义角色
[
	{
		"role" : "myRole2",
		"db" : "t3",
		"isBuiltin" : false,
		"roles" : [
			{
				"role" : "read",
				"db" : "t3"
			}
		],
		"inheritedRoles" : [
			{
				"role" : "read",
				"db" : "t3"
			}
		]
	}
]

# 删除当前库下的所有自定义角色
> db.dropAllRoles()
NumberLong(1)
> db.getRoles()   # 一个不剩
[ ]

that's all,see also:

https://docs.mongodb.com/v3.6/reference/method/js-role-management/ | https://docs.mongodb.com/v3.6/reference/method/js-user-management/