1-MongoDB - 基于角色的访问控制
访问控制
MongoDB3.6.12 + centos7.9
MongoDB提供了各种功能,例如身份验证,访问控制,加密,以保护MongoDB的数据安全。
这里我们重点介绍基于角色的访问控制,也就是MongoDB中的用户管理。
MongoDB使用基于角色的访问控制(RBAC)来管理对MongoDB系统的访问。授予用户一个或多个角色,这些角色确定用户对数据库资源和操作的访问权限。
启用访问控制
MongoDB默认情况下不启用访问控制。但可以使用--auth
或 security.authorization
设置启用授权。
启用访问控制后,用户必须对自己进行身份验证。
用户和角色
我们可以在创建用户时,同时指定角色,明确用户对于哪些资源可以进行操控。
我们也可以为现有用户授权或者撤销角色,后续会具体展开讲解。
内置角色和权限
MongoDB提供了内置角色,可提供数据库系统中通常所需的不同访问级别。
接下来简要介绍MongoDB中的内置角色和内置权限。
内置权限
Permission | Description |
---|---|
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权限 |
内置角色
Role | Description |
---|---|
超级用户角色 | 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在用户安全方面的重要一部分,用户在登录时,必须加上认证库才能操作。
建议,请将认证库指定为使用的库。
用户管理的相关方法
Name | Description |
---|---|
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
创建用户
创建用户语法参考:
{
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
用户来创建其他的用户。
# 创建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
参数开启用户认证:
[mongod@cs ~]$ mongod -f /data/mongodb_data/27017/conf/mongodb.conf --fork --auth
第二种,则是直接在配置文件中进行配置:
[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 ===
当开启认证之后,登录就要加认证了:
[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
不推荐上面的认证方式,通常在登录时加上认证:
# 标准登录语法,当然,你不要在 -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
认证成功,可以通过以下几个命令来查看用户了:
# 所有的用户都存在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()
更改用户
如果仅更改用户密码:
# 首先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
更改用户其他信息:
> 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
参数用来更新用户的角色信息,比如为一个无角色用户添加角色,更改角色都用该参数指定。
删除用户
删除用户有以下几种方式:
# 首先 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) # 结果提示有两个用户被删除
角色管理
角色管理的相关方法
Name | Description |
---|---|
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
查看角色
查看角色有两个方法:
db.getRoles() # 查看当前库下所有的自定义角色
db.getRole("read") # 查看指定内置或者自定义角色
默认的, MongoDB为每个可用的数据库,绑定所有内置的角色:
[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
方法来创建自定义角色,相关语法:
# 语法参考
{
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 } # 写确认和写超时
)
来个示例:
[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"
}
]
}
更改角色
为现有的自定义角色授予新的特权
# 先创建一个自定义角色
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"
]
}
]
为现有自定义角色删除指定特权
# 现在,自定义角色具有以下特权
> 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"]}
]
)
将内置或自定义角色赋予自定义的角色
# 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"
}
]
}
从现有自定义角色中删除内置或自定义角色
# 现在,myRole1 角色有如下角色
> db.getRole("myRole1")["roles"]
[
{
"role" : "myRole2",
"db" : "t3"
},
{
"role" : "readWrite",
"db" : "t3"
}
]
> db.revokeRolesFromRole("myRole1", ["readWrite", "myRole2"])
> db.getRole("myRole1")["roles"]
[ ]
更新角色
# 现在的 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"
}
]
}
删除角色
删除角色非常简单:
# 现在 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/