mysql 系统数据库包括几个授权表,包含用户账户和它们持有的权限信息。本节描述这些表。有关系统数据库中的其他表的信息,请参阅 第 7.3 节,“mysql 系统架构”。
这里的讨论描述了授权表的底层结构和服务器如何在与客户端交互时使用它们的内容。但是,通常您不直接修改授权表。修改是间接地通过使用账户管理语句,如 CREATE USER
、GRANT
和 REVOKE
,来设置账户和控制每个账户的权限。请参阅 第 15.7.1 节,“账户管理语句”。当您使用这些语句来执行账户操作时,服务器将代表您修改授权表。
使用语句,如 INSERT
、UPDATE
或 DELETE
,直接修改授权表是被禁止的,并且是在您自己的风险下进行的。服务器可以忽略由于这种修改而变得畸形的行。
对于任何修改授权表的操作,服务器都会检查表是否具有预期的结构,并在不符合时产生错误。要更新表以达到预期的结构,请执行 MySQL 升级过程。请参阅 第 3 章,《升级 MySQL》。
这些 mysql 数据库表包含授权信息:
-
user
:用户账户、静态全局权限和其他非权限列。 -
global_grants
:动态全局权限。 -
db
:数据库级权限。 -
tables_priv
:表级权限。 -
columns_priv
:列级权限。 -
procs_priv
: 存储过程和函数权限。 -
proxies_priv
: 代理用户权限。 -
default_roles
: 默认用户角色。 -
role_edges
: 角色子图边缘。 -
password_history
: 密码更改历史记录。
有关静态和动态全局权限之间的差异的信息,请参阅 静态与动态权限。
在 MySQL 8.3 中,授予权限表使用 InnoDB
存储引擎并且是事务性的。在 MySQL 8.3 之前,授予权限表使用 MyISAM
存储引擎并且是非事务性的。该更改使得伴随着账户管理语句的行为发生变化,例如 CREATE USER
或 GRANT
。以前,一个账户管理语句可以成功地命名多个用户,而现在,每个语句都是事务性的,或者成功地命名所有用户,或者回滚并且不产生任何效果。
每个授予权限表包含范围列和权限列:
-
范围列确定每行在表中的作用域,即应用的上下文。例如,
user
表的一行具有Host
和User
值的'h1.example.net'
和'bob'
,适用于从主机h1.example.net
连接到服务器的认证连接,客户端指定的用户名为bob
。类似地,db
表的一行具有Host
、User
和Db
列值的'h1.example.net'
、'bob'
和'reports'
,适用于bob
从主机h1.example.net
连接到服务器以访问reports
数据库。tables_priv
和columns_priv
表包含范围列,指示每行应用于哪些表或表/列组合。procs_priv
范围列指示每行应用于哪些存储过程。 -
权限列指示每行授予的权限,即允许执行的操作。服务器将各种授予权限表中的信息组合成用户的完整权限描述。第 8.2.7 节,“访问控制,第二阶段:请求验证” 描述了这些规则。
此外,授予权限表可能包含用于其他目的的列,而不仅仅是范围或权限评估。
服务器使用授予权限表的方式如下:
-
user
表的范围列确定是否拒绝或允许传入连接。对于允许的连接,user
表中的任何权限授予都表示用户的静态全局权限。该表中的任何权限授予都适用于服务器上的所有数据库。Caution因为任何静态全局权限都被视为所有数据库的权限,因此任何静态全局权限都使用户能够使用
SHOW DATABASES
或检查SCHEMATA
表中的数据库名称,除非数据库在数据库级别受到部分撤销的限制。 -
global_grants
表列出了当前分配给用户账户的动态全局权限。对于每行,范围列确定哪个用户拥有权限列中的权限。 -
db
表的范围列确定哪些用户可以从哪些主机访问哪些数据库。权限列确定允许的操作。数据库级别的权限授予适用于数据库和数据库中的所有对象,例如表和存储程序。 -
表
tables_priv
和columns_priv
与db
表类似,但它们更细粒度:它们在表和列级别上应用,而不是在数据库级别上。表级别上的权限授予适用于该表及其所有列。列级别上的权限授予仅适用于特定列。 -
表
procs_priv
适用于存储例程(存储过程和函数)。routine 级别上的权限授予仅适用于单个过程或函数。 -
表
proxies_priv
指示哪些用户可以代理其他用户,并且用户是否可以授予其他用户PROXY
权限。 -
表
default_roles
和role_edges
包含角色关系信息。 -
表
password_history
保留以前选择的密码,以便限制密码重用。见 第 8.2.15 节,“密码管理”。
服务器在启动时将授权表的内容读取到内存中。您可以通过发出 FLUSH PRIVILEGES
语句或执行 mysqladmin flush-privileges 或 mysqladmin reload 命令来重新加载授权表。授权表的更改将如 第 8.2.13 节,“何时生效的权限更改” 所示。
当您修改账户时,验证您的更改是否产生了预期的效果是一个好主意。要检查某个账户的权限,请使用 SHOW GRANTS
语句。例如,要确定用户名和主机名值为 bob
和 pc84.example.com
的账户的权限,请使用以下语句:
SHOW GRANTS FOR 'bob'@'pc84.example.com';
要显示账户的非权限属性,请使用 SHOW CREATE USER
:
SHOW CREATE USER 'bob'@'pc84.example.com';
服务器在访问控制的第一和第二阶段使用 user
和 db
表(见 第 8.2 节,“访问控制和账户管理”)。以下是 user
和 db
表的列。
表 8.4 user 和 db 表列
Table Name | user |
db |
---|---|---|
Scope columns | 主机 |
主机 |
用户 |
Db |
|
用户 |
||
Privilege columns | Select_priv |
Select_priv |
Insert_priv |
Insert_priv |
|
Update_priv |
Update_priv |
|
Delete_priv |
Delete_priv |
|
Index_priv |
Index_priv |
|
Alter_priv |
Alter_priv |
|
Create_priv |
Create_priv |
|
Drop_priv |
Drop_priv |
|
Grant_priv |
Grant_priv |
|
Create_view_priv |
Create_view_priv |
|
Show_view_priv |
Show_view_priv |
|
Create_routine_priv |
Create_routine_priv |
|
Alter_routine_priv |
Alter_routine_priv |
|
Execute_priv |
Execute_priv |
|
Trigger_priv |
Trigger_priv |
|
事件_priv |
事件_priv |
|
Create_tmp_table_priv |
Create_tmp_table_priv |
|
Lock_tables_priv |
Lock_tables_priv |
|
References_priv |
References_priv |
|
Reload_priv |
||
Shutdown_priv |
||
Process_priv |
||
File_priv |
||
Show_db_priv |
||
Super_priv |
||
Repl_slave_priv |
||
Repl_client_priv |
||
Create_user_priv |
||
Create_tablespace_priv |
||
Create_role_priv |
||
Drop_role_priv |
||
Security columns | ssl_type |
|
ssl_cipher |
||
x509_issuer |
||
x509_subject |
||
插件 |
||
身份验证字符串 |
||
密码已过期 |
||
密码最后更改时间 |
||
密码生命周期 |
||
账户锁定 |
||
密码重用历史记录 |
||
密码重用时间 |
||
密码需要当前 |
||
用户属性 |
||
Resource control columns | 最大查询数 |
|
最大更新数 |
||
最大连接数 |
||
最大用户连接数 |
The user
表的 plugin
和 身份验证字符串
列存储身份验证插件和凭据信息。
服务器使用 plugin
列中的插件名称来验证连接尝试的账户。
The plugin
列不能为空。启动时和在执行 FLUSH PRIVILEGES
时,服务器检查 user
表行。对于任何空 plugin
列的行,服务器将写入错误日志的警告信息:
[Warning] User entry 'user_name'@'host_name' has an empty plugin
value. The user will be ignored and no one can login with this user
anymore.
要将插件分配给缺少插件的账户,请使用 ALTER USER
语句。
The 密码已过期
列允许 DBA 将账户密码设为过期,并要求用户重置密码。默认 密码已过期
值为 'N'
,但可以使用 ALTER USER
语句将其设置为 'Y'
。在账户密码过期后,在服务器的后续连接中执行的所有操作将导致错误,直到用户发出 ALTER USER
语句以建立新的账户密码。
虽然可以通过将密码重置为当前值来“重置”已过期的密码,但出于良好政策的考虑,最好选择不同的密码。DBA 可以通过建立适当的密码重用策略来强制执行非重用。请参阅 密码重用策略。
密码最后更改时间
是一个 TIMESTAMP
列,指示密码最后更改的时间。该值仅对使用 MySQL 内置身份验证插件(mysql_native_password
已弃用,sha256_password
或 caching_sha2_password
)的账户非空。对于其他账户,例如使用外部身份验证系统的账户,该值为空。
密码最后更改时间
由 CREATE USER
、ALTER USER
和 SET PASSWORD
语句更新,并由 GRANT
语句创建账户或更改账户密码时更新。
password_lifetime
表示账户密码生命周期,以天为单位。如果密码超过生命周期(根据 password_last_changed
列评估),服务器将在客户端使用该账户连接时认为密码已过期。值 N
大于零意味着密码必须每 N
天更改一次。值为 0 禁用自动密码过期。如果值为 NULL
(默认),则应用全局过期策略,如 default_password_lifetime
系统变量所定义。
account_locked
表示账户是否被锁定(见 第 8.2.20 节,“账户锁定”)。
Password_reuse_history
是账户的 PASSWORD HISTORY
选项的值,或者为 NULL
,表示默认历史记录。
Password_reuse_time
是账户的 PASSWORD REUSE INTERVAL
选项的值,或者为 NULL
,表示默认间隔。
Password_require_current
对应账户的 PASSWORD REQUIRE
选项的值,如下表所示。
表 8.5 允许的 Password_require_current 值
Password_require_current Value | Corresponding PASSWORD REQUIRE Option |
---|---|
'Y' |
PASSWORD REQUIRE CURRENT |
'N' |
PASSWORD REQUIRE CURRENT OPTIONAL |
NULL |
PASSWORD REQUIRE CURRENT DEFAULT |
User_attributes
是一个 JSON 格式的列,存储账户属性,而不是存储在其他列中。INFORMATION_SCHEMA
通过 USER_ATTRIBUTES
表公开这些属性。
The User_attributes
列可能包含以下属性:
-
additional_password
:二级密码,如果有。见 双密码支持。 -
Restrictions
:限制列表,如果有。限制是通过部分撤销操作添加的。该属性值是一个数组,其中每个元素都有Database
和Restrictions
键,指示受限数据库的名称和适用限制(见 第 8.2.12 节,“权限限制使用部分撤销”)。 -
Password_locking
:失败登录跟踪和临时账户锁定的条件,如果有(见 失败登录跟踪和临时账户锁定)。该Password_locking
属性根据FAILED_LOGIN_ATTEMPTS
和PASSWORD_LOCK_TIME
选项的CREATE USER
和ALTER USER
语句更新。该属性值是一个哈希,具有failed_login_attempts
和password_lock_time_days
键,指示已指定的选项值。如果键不存在,其值隐式为 0。如果键值隐式或显式为 0,则相应的功能被禁用。 -
multi_factor_authentication
:mysql.user
系统表中的行具有plugin
列,指示身份验证插件。对于单因素身份验证,该插件是唯一的身份验证因素。但是,对于双因素或三因素的多因素身份验证,该插件对应于第一个身份验证因素,但需要存储第二个和第三个因素的附加信息。multi_factor_authentication
属性持有该信息。The
multi_factor_authentication
值是一个数组,其中每个数组元素是一个哈希,描述身份验证因素使用以下属性:-
plugin
:身份验证插件的名称。 -
authentication_string
:身份验证字符串值。 -
passwordless
:一个标志,指示用户是否应使用安全令牌作为唯一的身份验证方法,而不使用密码。 -
requires_registration
:一个定义用户账户是否注册了安全令牌的标志。
数组的第一个和第二个元素描述多因素身份验证因素2和3。
-
如果没有应用属性,则 User_attributes
为 NULL
。
示例:一个具有辅助密码和部分撤销数据库权限的账户在列值中具有 additional_password
和 Restrictions
属性:
mysql> SELECT User_attributes FROM mysql.User WHERE User = 'u'\G
*************************** 1. row ***************************
User_attributes: {"Restrictions":
[{"Database": "mysql", "Privileges": ["SELECT"]}],
"additional_password": "hashed_credentials"}
要确定哪些属性存在,请使用 JSON_KEYS()
函数:
SELECT User, Host, JSON_KEYS(User_attributes)
FROM mysql.user WHERE User_attributes IS NOT NULL;
要提取特定的属性,例如 Restrictions
,请执行以下操作:
SELECT User, Host, User_attributes->>'$.Restrictions'
FROM mysql.user WHERE User_attributes->>'$.Restrictions' <> '';
以下是存储在 multi_factor_authentication
中的信息示例:
{
"multi_factor_authentication": [
{
"plugin": "authentication_ldap_simple",
"passwordless": 0,
"authentication_string": "ldap auth string",
"requires_registration": 0
},
{
"plugin": "authentication_webauthn",
"passwordless": 0,
"authentication_string": "",
"requires_registration": 1
}
]
}
在访问控制的第二阶段,服务器执行请求验证,以确保每个客户端都具有足够的权限来发出每个请求。在 user
和 db
授予权表之外,服务器还可能会咨询 tables_priv
和 columns_priv
表,以便对涉及表的请求进行更细粒度的权限控制。这些表具有以下列。
表 8.6 tables_priv 和 columns_priv 表列
Table Name | tables_priv |
columns_priv |
---|---|---|
Scope columns | 主机 |
主机 |
Db |
Db |
|
User |
User |
|
Table_name |
Table_name |
|
Column_name |
||
Privilege columns | Table_priv |
Column_priv |
Column_priv |
||
Other columns | Timestamp |
Timestamp |
Grantor |
Timestamp
和 Grantor
列分别设置为当前时间戳和 CURRENT_USER
值,但否则未使用。
对于涉及存储例程的请求验证,服务器可能会咨询 procs_priv
表,该表具有以下列。
表 8.7 procs_priv 表列
Table Name | procs_priv |
---|---|
作用域列 | 主机 |
Db |
|
User |
|
Routine_name |
|
Routine_type |
|
权限列 | Proc_priv |
其他列 | Timestamp |
Grantor |
Routine_type
列是一个 ENUM
列,具有 'FUNCTION'
或 'PROCEDURE'
值,以指示该行引用的例程类型。这使得可以单独授予函数和同名过程的权限。
Timestamp
和 Grantor
列未使用。
proxies_priv 表记录代理账户的信息。它具有以下列:
要使账户能够授予其他账户PROXY
权限,它必须在proxies_priv
表中有一行,With_grant
设置为1,Proxied_host
和Proxied_user
设置为指示授予权限的账户或账户。例如,在MySQL安装期间创建的'root'@'localhost'
账户在proxies_priv
表中有一行,启用授予PROXY
权限给''@''
,即所有用户和所有主机。这使得root
能够设置代理用户,以及将设置代理用户的权限委托给其他账户。见第8.2.19节,“代理用户”。
global_grants 表列出了当前分配给用户账户的动态全局权限。该表具有以下列:
-
USER
,HOST
:授予权限的账户的用户名和主机名。 -
PRIV
:权限名称。 -
WITH_GRANT_OPTION
:账户是否可以授予该权限给其他账户。
default_roles 表列出了默认用户角色。它具有以下列:
-
HOST
,USER
:应用默认角色的账户或角色。 -
DEFAULT_ROLE_HOST
,DEFAULT_ROLE_USER
:默认角色。
role_edges 表列出了角色子图的边缘。它具有以下列:
-
FROM_HOST
,FROM_USER
:授予权限的账户。 -
TO_HOST
,TO_USER
:授予权限的角色。 -
WITH_ADMIN_OPTION
:账户是否可以授予该角色给其他账户,并使用WITH ADMIN OPTION
撤销该角色。
password_history 表包含密码更改信息。它具有以下列:
-
Host
,User
:密码更改的账户。 -
Password_timestamp
:密码更改的时间。 -
Password
:新密码哈希值。
password_history 表累积了每个账户的足够数量的非空密码,以便MySQL执行密码历史记录长度和重用间隔检查。密码更改尝试时自动修剪超出限制的条目。
空密码不计入密码历史记录,并且可以随时重用。
如果账户被重命名,其条目将被重命名以匹配。如果账户被删除或其身份验证插件被更改,其条目将被删除。
授予权限表中的范围列包含字符串。每个列的默认值为空字符串。以下表显示每个列允许的字符数。
表 8.8 授予权限表范围列长度
Column Name | Maximum Permitted Characters |
---|---|
Host , Proxied_host |
255 |
User , Proxied_user |
32 |
Db |
64 |
表名 |
64 |
列名 |
64 |
例程名 |
64 |
主机
和 代理主机
值在存储在授权表之前将被转换为小写。
对于访问检查目的,用户
、代理用户
、身份验证字符串
、数据库
和 表名
值的比较是区分大小写的。比较 主机
、代理主机
、列名
和 例程名
值是不区分大小写的。
在 user
和 db
表中,每个权限在一个单独的列中被声明为 ENUM('N','Y') DEFAULT 'N'
。换言之,每个权限可以被禁用或启用,默认情况下是禁用。
在 tables_priv
、columns_priv
和 procs_priv
表中,权限列被声明为 SET
列。这些列中的值可以包含表控制的任何权限组合。只有列值中列出的权限被启用。
表 8.9 set 类型权限列值
Table Name | Column Name | Possible Set Elements |
---|---|---|
tables_priv |
表权限 |
'Select', 'Insert', 'Update', 'Delete', 'Create', 'Drop', 'Grant', 'References', 'Index', 'Alter', 'Create View', 'Show view', 'Trigger' |
tables_priv |
列权限 |
'Select', 'Insert', 'Update', 'References' |
columns_priv |
列权限 |
'Select', 'Insert', 'Update', 'References' |
procs_priv |
例程权限 |
'Execute', 'Alter Routine', 'Grant' |
只有 user
和 global_grants
表指定了管理权限,如 RELOAD
、SHUTDOWN
和 SYSTEM_VARIABLES_ADMIN
。管理操作是对服务器本身的操作,而不是数据库特定的,因此没有理由在其他授权表中列出这些权限。因此,服务器只需要咨询 user
和 global_grants
表来确定用户是否可以执行管理操作。
FILE
权限也只在 user
表中指定。它不是管理权限,但用户在服务器主机上的读取或写入文件的能力独立于访问的数据库。
为了允许 MySQL 授权表的并发 DML 和 DDL 操作,读操作以前获取行锁的 MySQL 授权表现在执行为非锁定读取。这些操作包括:
-
SELECT
语句和其他只读语句,它们通过连接列表和子查询从授权表中读取数据,包括SELECT ... FOR SHARE
语句,使用任何事务隔离级别。 -
DML 操作,它们从授权表中读取数据(通过连接列表或子查询),但不修改它们,使用任何事务隔离级别。
语句不再获取行锁时读取授权表数据,将报告警告,如果使用语句基于复制。
使用 -binlog_format=mixed
,DML 操作从授权表中读取数据将被写入二进制日志作为行事件,以使操作安全地用于混合模式复制。
SELECT ... FOR SHARE
语句从授权表中读取数据将报告警告。使用 FOR SHARE
子句,不支持在授权表上的读锁。
使用SERIALIZABLE
隔离级别执行的读取授权表数据的DML操作将报告警告。在授权表上,不支持使用SERIALIZABLE
隔离级别通常获取的读锁。