MongoDB 用户管理

背景

最近在扫盲MongoDB 4.2 的相关知识点,顺便记录下日常的一些操作。包括:用户管理、索引管理、引擎管理、副本集管理、分片管理等。本文对MongoDB的用户管理进行说明。

本文MongoDB的配置文件模板:

systemLog:
   verbosity: 0
   quiet: false
   traceAllExceptions: false
   syslogFacility: user
   path: /data/mongodb/logs/mongodb.log
   logAppend: true
   logRotate: rename
   destination: file
   timeStampFormat: iso8601-local
   component:
      accessControl:
         verbosity: 0
      command:
         verbosity: 0
      control:
         verbosity: 0
      ftdc:
         verbosity: 0
      geo:
         verbosity: 0
      index:
         verbosity: 0
      network:
         verbosity: 0
      query:
         verbosity: 0
      replication:
         verbosity: 0
         election:
            verbosity: 0
         heartbeats:
            verbosity: 0
         initialSync:
            verbosity: 0
         rollback:
            verbosity: 0
      sharding:
          verbosity: 0
      storage:
         verbosity: 0
         journal:
            verbosity: 0
         recovery:
            verbosity: 0
      transaction:
         verbosity: 0
      write:
         verbosity: 0

processManagement:
   fork: true
   pidFilePath: /data/mongodb/data/mongodb.pid
#   timeZoneInfo: <string>

net:
   port: 27017
   bindIp: 0.0.0.0
   maxIncomingConnections: 65536
   wireObjectCheck: true
   ipv6: false
   unixDomainSocket:
      enabled: true
      pathPrefix: /tmp
      filePermissions: 0700
   tls:
      mode: disabled
   compression:
      compressors: snappy,zstd,zlib
   serviceExecutor: synchronous

#security:
#   keyFile: <string>
#   clusterAuthMode: keyFile
#   authorization: enabled
#   transitionToAuth: false
#   javascriptEnabled: true
#   redactClientLogData: <boolean>
#   clusterIpSourceWhitelist:
#     - 127.0.0.1
#     - ::1

storage:
   dbPath: /data/mongodb/data/
   indexBuildRetry: true
   journal:
      enabled: true
      commitIntervalMs: 500
   directoryPerDB: true
   syncPeriodSecs: 60
   engine: wiredTiger
   wiredTiger:
      engineConfig:
#         cacheSizeGB: <number>
         journalCompressor: snappy
         directoryForIndexes: false
operationProfiling:
   # 指定应分析哪些操作,默认off:分析器已关闭,并且不收集任何数据;slowOp:收集比slowms的时间长的数据;all:收集所有操作的数据
   mode: slowOp
   slowOpThresholdMs: 100
   slowOpSampleRate: 1

replication:
   # 复制日志(oplog)的最大大小(以M为单位),仅适用于mongod。操作日志通常是可用磁盘空间的5%,更改正在运行的副本集成员的操作日志大小,请使用replSetResizeOplog管理命令。
   oplogSizeMB: 100
   # replSetName: <string>

#sharding:
   # 分片群集中扮演的角色,可选值configsvr:配置服务器,端口27019开始;shardsvr:分片,端口27018开始;需要将实例部署为副本集成员,使用replSetName设置并指定副本集的名称,仅适用于mongod。
   # clusterRole: <string>
   # configDB: <string>

View Code

用户管理

在一个新的实例上,创建的第一个用户应该是具有管理其他用户权限的用户管理员。在MongoDB部署上启用访问控制会强制执行身份验证,要求用户认证自己,用户只能执行根据其角色确定的操作。具体的用户管理可以看官方文档或则MongoDB用户创建,本文做下补充。

1,创建用户:db.createUser(user, writeConcern)

user:包含有关要创建的用户的身份验证和访问信息。
writeConcern:可选,创建操作的写关注级别。 writeConcern文档的字段与getLastError命令的字段相同。

MongoDB4.2开始,可以使用passwordPrompt()方法来提示输入密码,不需要在命令中指定密码。但仍然可以像使用早期版本的MongoDB一样直接指定密码。格式:

{
  user: "<name>",   -- 用户名
  pwd: passwordPrompt(),    -- 提示输入密码,也可以直接写密码
  customData: { <any information> },  -- 可选,可用于存储与此特定用户关联的任何数据。 例如,这可以是用户的全名或员工ID。
  roles: [
    { role: "<role>", db: "<database>" } | "<role>",
    ...
  ],     -- 授予用户的角色。 可以指定一个空数组[]来创建没有角色的用户
  authenticationRestrictions: [
     {
       clientSource: ["<IP>" | "<CIDR range>", ...],
       serverAddress: ["<IP>" | "<CIDR range>", ...]
     },
     ...
  ],  -- 可选,服务器对创建的用户强制执行的身份验证限制,指定允许用户连接到服务器或服务器可以接受用户的IP地址和CIDR(无类别域间路由)范围的列表
  mechanisms: [ "<SCRAM-SHA-1|SCRAM-SHA-256>", ... ],  --可选,指定特定的SCRAM机制或用于创建SCRAM用户凭据的机制
  passwordDigestor: "<server|client>"  --可选,指服务器还是客户端提取密码。
}

其中在roles字段中,可以指定内置角色用户定义的角色:

Built-In Roles(内置角色):
    1. 数据库用户角色:read、readWrite;
    2. 数据库管理角色:dbAdmin、dbOwner、userAdmin;
    3. 集群管理角色:clusterAdmin、clusterManager、clusterMonitor、hostManager;
    4. 备份恢复角色:backuprestore5. 所有数据库角色:readAnyDatabase、readWriteAnyDatabase、userAdminAnyDatabase、dbAdminAnyDatabase
    6. 超级用户角色:root  
    // 这里还有几个角色间接或直接提供了系统超级用户的访问(dbOwner 、userAdmin、userAdminAnyDatabase)
    7. 内部角色:__system

具体权限

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

创建用户:

db.createUser(
{
user:'dba',
pwd:passwordPrompt(),
roles:[{role:'root',db:'admin'}],
customData:{name:'运维账号'},
authenticationRestrictions: [ {
        clientSource: ["192.168.163.134"],
        serverAddress: ["192.168.163.134"]
     } ]
}
)

说明创建一个用户名为dba的管理角色(root)的账号,且备注为运维账号,并只能在IP为192.168.163.134的主机上访问。

use xxx
db.createUser(
{
user:'zjy',
pwd:passwordPrompt(),
roles:[{role:'readAnyDatabase',db:'admin'},
"readWrite"
],
customData:{name:'运维账号'},
authenticationRestrictions: [ {
        clientSource: ["192.168.163.134"],
        serverAddress: ["192.168.163.134"]
     } ]
},
{ w: "majority" , wtimeout: 5000 }
)

说明创建一个用户名为zjy的账号,对当前库(xxx)有readWrite权限,对指定库(admin)有readAnyDatabase权限;且备注为运维账号,并只能在IP为192.168.163.134的主机上访问。

关于账号权限的一些说明可以看MongoDB用户创建

2,修改用户密码:db.changeUserPassword(username,password)

db.changeUserPassword('user','pwd')

3,查看指定用户:db.getUser(username, args)

格式:

db.getUser( "<username>", {
   showCredentials: <Boolean>,
   showPrivileges: <Boolean>,
   showAuthenticationRestrictions: <Boolean>,
   filter: <document>
} )

其中”<username>”表示数据库用户,其他的参数都是可选项:

  • showCredentials:查看用户证书
  • showPrivileges:查看用户权限
  • showAuthenticationRestrictions:查看用户限制条件
> db.getUser('dba')
{
    "_id" : "admin.dba",
    "userId" : UUID("6a1c2217-0c5b-4aa6-a29b-de50ac1d84ae"),
    "user" : "dba",
    "db" : "admin",
    "customData" : {
        "name" : "运维账号abc"
    },
    "roles" : [
        {
            "role" : "root",
            "db" : "admin"
        }
    ],
    "mechanisms" : [
        "SCRAM-SHA-1",
        "SCRAM-SHA-256"
    ]
}

> db.getUser('dba',{showPrivileges:1})
...
...

4,查看当前库的所有用户:db.getUsers(<options>)

> db.getUsers() == show users
[
    {
        "_id" : "xxx.zjy",
        "userId" : UUID("2ad9f4c9-e156-4e71-b8f9-3915beb322e8"),
        "user" : "zjy",
        "db" : "xxx",
        "customData" : {
            "name" : "运维账号"
        },
        "roles" : [
            {
                "role" : "readAnyDatabase",
                "db" : "admin"
            },
            {
                "role" : "readWrite",
                "db" : "xxx"
            }
        ],
        "mechanisms" : [
            "SCRAM-SHA-1",
            "SCRAM-SHA-256"
        ]
    }
]

5,移除、删除指定用户:db.removeUser(username)db.dropUser(username, writeConcern)

> db.removeUser('zjy')
WARNING: db.removeUser has been deprecated, please use db.dropUser instead  --不推荐使用
true

> db.dropUser('accountAdmin01',{w: "majority", wtimeout: 5000})
true

如果在副本集上运行,则默认情况下使用多数写关注来执行db.dropUser()。

6,删除当前库所有用户:db.dropAllUsers(writeConcern)

> db.dropAllUsers({w: "majority", wtimeout: 5000})
NumberLong(4)

7,更新指定用户:db.updateUser(username, update, writeConcern)

格式:

db.updateUser(
   "<username>",
   {
     customData : { <any information> },
     roles : [
       { role: "<role>", db: "<database>" } | "<role>",
       ...
     ],
     pwd: passwordPrompt(),      // Or  "<cleartext password>"
     authenticationRestrictions: [
        {
          clientSource: ["<IP>" | "<CIDR range>", ...],
          serverAddress: ["<IP>", | "<CIDR range>", ...]
        },
        ...
     ],
     mechanisms: [ "<SCRAM-SHA-1|SCRAM-SHA-256>", ... ],
     passwordDigestor: "<server|client>"
   },
   writeConcern: { <write concern> }
)

上面各个参数的意思和createUser一致。

> db.updateUser(
... 'user123',
... {
... pwd:passwordPrompt(),
... roles:['read'],
... customData:{name:'运维账号'},
... authenticationRestrictions: [ {
...         clientSource: ["192.168.163.134"],
...         serverAddress: ["192.168.163.134"]
...      } ]
... },
... { w: "majority" , wtimeout: 5000 }
... )

8,授权role给指定用户:db.grantRolesToUser(username, roles, writeConcern)

格式:

db.grantRolesToUser( "<username>", [ <roles> ], { <writeConcern> } )
> show users;
{
    "_id" : "products.user123",
    "userId" : UUID("b71e3c9d-1e57-4178-b6f8-48445ee2ebfc"),
    "user" : "user123",
    "db" : "products",
    "roles" : [
        {
            "role" : "read",
            "db" : "products"
        }
    ],
    "customData" : {
        "name" : "运维账号"
    },
    "mechanisms" : [
        "SCRAM-SHA-1",
        "SCRAM-SHA-256"
    ]
}
> db.grantRolesToUser('user123',['readWrite',{role:'read',db:'test'}])
> show users; { "_id" : "products.user123", "userId" : UUID("b71e3c9d-1e57-4178-b6f8-48445ee2ebfc"), "user" : "user123", "db" : "products", "roles" : [ { "role" : "readWrite", "db" : "products" }, { "role" : "read", "db" : "test" }, { "role" : "read", "db" : "products" } ], "customData" : { "name" : "运维账号" }, "mechanisms" : [ "SCRAM-SHA-1", "SCRAM-SHA-256" ] }

说明:在已有用户(user123)的权限下,新增当前库(products)的readWrite role和test库的read role。

9,移除指定用户的role:db.revokeRolesFromUser()

格式:

db.revokeRolesFromUser( "<username>", [ <roles> ], { <writeConcern> } )
> show users;
{
    "_id" : "products.user123",
    "userId" : UUID("b71e3c9d-1e57-4178-b6f8-48445ee2ebfc"),
    "user" : "user123",
    "db" : "products",
    "roles" : [
        {
            "role" : "readWrite",
            "db" : "products"
        },
        {
            "role" : "read",
            "db" : "test"
        },
        {
            "role" : "read",
            "db" : "products"
        }
    ],
    "customData" : {
        "name" : "运维账号"
    },
    "mechanisms" : [
        "SCRAM-SHA-1",
        "SCRAM-SHA-256"
    ]
}

> db.revokeRolesFrom
db.revokeRolesFromRole(  db.revokeRolesFromUser(

> db.revokeRolesFromUser('user123',[{role:'read',db:'test'},'readWrite'])

> show users; { "_id" : "products.user123", "userId" : UUID("b71e3c9d-1e57-4178-b6f8-48445ee2ebfc"), "user" : "user123", "db" : "products", "roles" : [ { "role" : "read", "db" : "products" } ], "customData" : { "name" : "运维账号" }, "mechanisms" : [ "SCRAM-SHA-1", "SCRAM-SHA-256" ] }

说明:移除已有用户(user123)在当前库(products)的readWrite role和test库的read role权限。

10,用户认证:db.auth(username,password)

> db.auth('dba','DBA')
1

以上说明的role都是内置角色,用户自定义角色可以看官方文档说明。

角色管理

1,创建角色:db.createRole(role, writeConcern)

格式:

{
  role: "<name>",      --新角色的名称
  privileges: [
     { resource: { <resource> }, actions: [ "<action>", ... ] },
     ...
  ],      --权限,由resource和actions组成。空数组表示无权限
  roles: [
     { role: "<role>", db: "<database>" } | "<role>",
      ...
  ],     --角色的数组,此角色从中继承特权,空数组表示没有继承角色
  authenticationRestrictions: [
    {
      clientSource: ["<IP>" | "<CIDR range>", ...],
      serverAddress: ["<IP>" | "<CIDR range>", ...]
    },
    ...
  ]    --验证限制
}

resource表示资源,包括database或collection也可以是database和collection的组合:

{ db: <database>, collection: <collection> }

actions表示权限操作,”actions” 定义了”user”能够对 “resource document”执行的操作:

find、insert、remove、update

privilege表示权限,”privilege” 是一组”resource” 和 “actions” 的组,关于privileges的详细信息见官方说明,里面有各个命令的权限说明

>db.createRole(
   {role:"zhoujy", //角色名称
       privileges: [ // 权限集
        {resource: //资源 
         {db:"xxx",     // 创建的zhoujy角色具有对xxx库的操作权限,具体权限见actions
          collection:""   // xxx库下对应的集合名 如果为"" 表示所有集合
         },
          actions: [ "find", "insert", "remove","update" ]   //角色可进行的操作,注意这里是一个数组
        }
                   ],
         roles: [{ role: "read", db: "admin" }] // 是否继承其他的角色,如果指定了其他角色那么新创建的角色自动继承对应其他角色的所有权限,该参数必须显示指定
   },
  { w: "majority" , wtimeout: 5000 }
)

说明:创建了一个zhoujy的role,可以像内置角色一样来使用。

2,查看指定角色信息:db.getRole(rolename, args)

参数:

showBuiltinRoles:可选,在输出中返回内置角色信息

showPrivileges:可选,在输出中返回权限信息

> db.getRole('zhoujy')
{
    "role" : "zhoujy",
    "db" : "admin",
    "isBuiltin" : false,
    "roles" : [ ],
    "inheritedRoles" : [ ]
}

> db.getRole('zhoujy',{showPrivileges: true})
{
    "role" : "zhoujy",
    "db" : "admin",
    "isBuiltin" : false,
    "roles" : [ ],
    "inheritedRoles" : [ ],
    "privileges" : [
        {
            "resource" : {
                "db" : "xxx",
                "collection" : ""
            },
            "actions" : [
                "find",
                "insert",
                "remove",
                "update"
            ]
        }
    ],
    "inheritedPrivileges" : [
        {
            "resource" : {
                "db" : "xxx",
                "collection" : ""
            },
            "actions" : [
                "find",
                "insert",
                "remove",
                "update"
            ]
        }
    ]
}

3,查看当前数据库中所有的角色信息:db.getRoles()

不带参数运行,返回数据库用户定义角色的继承信息,参数:

rolesInfo:1,返回所有用户定义的角色信息

showPrivileges:true,返回所有用户的权限信息

showBuiltinRoles:true,返回内置角色信息

>db.getRoles(
    {
      rolesInfo: 1,
      showPrivileges:true,
      showBuiltinRoles: true
    }
)

4,角色授权:db.grantPrivilegesToRole(rolename, privileges, writeConcern)

> db.grantPrivilegesToRole(
  "zhoujy",
  [
    {
      resource: { db: "products", collection: "" },
      actions: [ "insert","find"  ]
    }
  ],
  { w: "majority" }
)

说明:在已有的role下面,再追加权限。

5,角色权限回收:db.revokePrivilegesFromRole(rolename, privileges, writeConcern)

> db.revokePrivilegesFromRole(
  "zhoujy",
  [
    {
      resource: { db: "products", collection: "" },
      actions: [ "insert" ]
    }
  ],
  { w: "majority" }
)

说明:在已有的role下面,删除权限。

6,角色继承:db.grantRolesToRole(rolename, roles, writeConcern)

> db.grantRolesToRole(  
          "zhoujy",
           [ "zhoujy1","zhoujy2" ],
           { w: "majority" , wtimeout: 5000 }
                    )

说明:将 zhoujy1、zhoujy2 两个角色(假设之前已经创建好)的权限授予zhoujy角色

7,继承角色回收: db.revokeRolesFromRole(rolename, roles, writeConcern)

db.revokeRolesFromRole(
          "zhoujy" ,
          [ "zhoujy2" ]
                      ) 

说明:撤销zhoujy角色从zhoujy2角色所继承的所有权限

8,更新角色:db.updateRole(rolename, update, writeConcern)

格式:

    "<rolename>",  --角色名
    {
      privileges:
          [
            { resource: { <resource> }, actions: [ "<action>", ... ] },
            ...
          ],  --权限
      roles:
          [
            { role: "<role>", db: "<database>" } | "<role>",
            ...
          ],   --角色
      authenticationRestrictions:
          [
            {
              clientSource: ["<IP>" | "<CIDR range>", ...],
              serverAddress: ["<IP>", | "<CIDR range>", ...]
            },
            ...
          ]
    },    -- 限制
    { <writeConcern> } --写关注
)
> db.updateRole(
   "zhoujy",
   {
      privileges: 
      [
        {
        resource:{db:"xxx", collection:""},
        actions: [ "find", "insert", "remove","update" ]
        }
       ],
      roles: []
   },
  { w: "majority" , wtimeout: 5000 }
)

9,删除角色:db.dropRole(rolename, writeConcern)

> db.dropRole('myClusterwideAdmin', { w: "majority" })
true

10,删除当前库所有用户自定义的角色:db.dropAllRoles(writeConcern)

db.dropAllRoles( { w: "majority" } )

到此,关于用户管理的已经介绍完,更多的详细信息可以见官网说明,或可以查阅MongoDB 权限控制系统简介需要注意的是,需要开启服务器参数中的security.authorization:true,才会使用用户验证(db.auth())。

总结

本文大致介绍了MongoDB用户管理的使用说明,后续根据需要会持续更新本文。关于用户管理的更多说明可以看官方文档

参考文章:

MongoDB 3.0 用户创建

MongoDB 权限控制系统简介