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 9.0 中,授权表使用 InnoDB
存储引擎并且是事务性的。在 MySQL 9.0 之前,授权表使用 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 节“访问控制,阶段 2:请求验证”描述了这方面的规则。
此外,授权表可能包含用于除作用域或权限评估之外的其他目的的列。
服务器按以下方式使用授权表:
user
表作用域列确定是拒绝还是允许传入连接。对于允许的连接,在user
表中授予的任何权限都表示用户的静态全局权限。此表中授予的任何权限都适用于服务器上的所有数据库。注意由于任何静态全局权限都被视为所有数据库的权限,因此任何静态全局权限都使用户能够使用
SHOW DATABASES
或通过检查INFORMATION_SCHEMA
的SCHEMATA
表来查看所有数据库名称,但已通过部分撤销在数据库级别受到限制的数据库除外。global_grants
表列出了当前分配给用户帐户的动态全局权限。对于每一行,作用域列确定哪个用户拥有权限列中命名的权限。db
表作用域列确定哪些用户可以从哪些主机访问哪些数据库。权限列确定允许的操作。在数据库级别授予的权限适用于该数据库以及该数据库中的所有对象,例如表和存储程序。tables_priv
和columns_priv
表类似于db
表,但粒度更细:它们应用于表和列级别,而不是数据库级别。在表级别授予的权限适用于该表及其所有列。在列级别授予的权限仅适用于特定列。procs_priv
表适用于存储例程(存储过程和函数)。在例程级别授予的权限仅适用于单个过程或函数。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';
服务器在访问控制的第一阶段和第二阶段都使用 mysql
数据库中的 user
和 db
表(请参阅 第 8.2 节“访问控制和帐户管理”)。user
和 db
表中的列如下所示。
表 8.4 user 和 db 表列
表名 | user |
db |
---|---|---|
作用域列 | Host |
Host |
User |
Db |
|
User |
||
权限列 | 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 |
|
Event_priv |
Event_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 |
||
安全列 | ssl_type |
|
ssl_cipher |
||
x509_issuer |
||
x509_subject |
||
plugin |
||
authentication_string |
||
password_expired |
||
password_last_changed |
||
password_lifetime |
||
account_locked |
||
Password_reuse_history |
||
Password_reuse_time |
||
Password_require_current |
||
User_attributes |
||
资源控制列 | max_questions |
|
max_updates |
||
max_connections |
||
max_user_connections |
user
表的 plugin
和 authentication_string
列存储身份验证插件和凭据信息。
服务器使用帐户行 plugin
列中命名的插件来对该帐户的连接尝试进行身份验证。
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
语句。
password_expired
列允许 DBA 使帐户密码过期并要求用户重置其密码。默认的 password_expired
值为 'N'
,但可以使用 ALTER USER
语句将其设置为 'Y'
。帐户密码过期后,在用户发出 ALTER USER
语句以建立新的帐户密码之前,该帐户在随后连接到服务器时执行的所有操作都将导致错误。
尽管可以通过将过期密码设置为其当前值来“重置”,但作为一项好的策略,最好选择一个不同的密码。DBA 可以通过建立适当的密码重用策略来强制执行不重用。请参阅 密码重用策略。
password_last_changed
是一个 TIMESTAMP
列,指示上次更改密码的时间。该值仅对于使用 MySQL 内置身份验证插件(sha256_password
或 caching_sha2_password
)的帐户才为非 NULL
。对于其他帐户(例如,使用外部身份验证系统进行身份验证的帐户),该值为 NULL
。
password_last_changed
由 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 值 | 对应的 PASSWORD REQUIRE 选项 |
---|---|
'Y' |
PASSWORD REQUIRE CURRENT |
'N' |
PASSWORD REQUIRE CURRENT OPTIONAL |
NULL |
PASSWORD REQUIRE CURRENT DEFAULT |
User_attributes
是一个 JSON 格式的列,用于存储未存储在其他列中的帐户属性。INFORMATION_SCHEMA
通过 USER_ATTRIBUTES
表公开这些属性。
User_attributes
列可能包含以下属性:
additional_password
:次要密码(如果有)。参见 双重密码支持。Restrictions
:限制列表(如果有)。限制是通过部分撤销操作添加的。属性值是一个元素数组,每个元素都有Database
和Restrictions
键,分别表示受限数据库的名称和对其应用的限制(请参见 第 8.2.12 节“使用部分撤销进行权限限制”)。Password_locking
:失败登录跟踪和临时帐户锁定的条件(如果有)(请参见 失败登录跟踪和临时帐户锁定)。Password_locking
属性根据CREATE USER
和ALTER USER
语句的FAILED_LOGIN_ATTEMPTS
和PASSWORD_LOCK_TIME
选项进行更新。属性值是一个哈希值,其中包含failed_login_attempts
和password_lock_time_days
键,分别表示为该帐户指定的这些选项的值。如果缺少某个键,则其值隐式为 0。如果某个键值隐式或显式为 0,则将禁用相应的 功能。multi_factor_authentication
:mysql.user
系统表中的行有一个plugin
列,用于指示身份验证插件。对于单因素身份验证,该插件是唯一的身份验证因素。对于双因素或三因素形式的多因素身份验证,该插件对应于第一个身份验证因素,但必须存储第二个和第三个因素的附加信息。multi_factor_authentication
属性保存此信息。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 表列
表名 | tables_priv |
columns_priv |
---|---|---|
作用域列 | Host |
Host |
Db |
Db |
|
User |
User |
|
Table_name |
Table_name |
|
Column_name |
||
权限列 | Table_priv |
Column_priv |
Column_priv |
||
其他列 | Timestamp |
Timestamp |
Grantor |
Timestamp
和 Grantor
列分别设置为当前时间戳和 CURRENT_USER
值,但在其他情况下未使用。
为了验证涉及存储例程的请求,服务器可以查阅 procs_priv
表,该表具有下表中所示的列。
表 8.7 procs_priv 表列
表名 | procs_priv |
---|---|
作用域列 | Host |
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 授权表范围列长度
列名 | 允许的最大字符数 |
---|---|
Host 、Proxied_host |
255 |
User 、Proxied_user |
32 |
Db |
64 |
Table_name |
64 |
Column_name |
64 |
Routine_name |
64 |
Host
和 Proxied_host
值在存储到授权表之前将转换为小写。
出于访问检查目的,User
、Proxied_user
、authentication_string
、Db
和 Table_name
值的比较区分大小写。Host
、Proxied_host
、Column_name
和 Routine_name
值的比较不区分大小写。
user
和 db
表在单独的列中列出每个权限,这些列声明为 ENUM('N','Y') DEFAULT 'N'
。换句话说,每个权限都可以被禁用或启用,默认情况下是被禁用的。
tables_priv
、columns_priv
和 procs_priv
表将权限列声明为 SET
列。这些列中的值可以包含该表控制的权限的任意组合。只有列值中列出的权限是启用的。
表 8.9 Set 类型权限列值
表名 | 列名 | 可能的 Set 元素 |
---|---|---|
tables_priv |
Table_priv |
'Select', 'Insert', 'Update', 'Delete', 'Create', 'Drop', 'Grant', 'References', 'Index', 'Alter', 'Create View', 'Show view', 'Trigger' |
tables_priv |
Column_priv |
'Select', 'Insert', 'Update', 'References' |
columns_priv |
Column_priv |
'Select', 'Insert', 'Update', 'References' |
procs_priv |
Proc_priv |
'Execute', 'Alter Routine', 'Grant' |
只有 user
和 global_grants
表指定了管理权限,例如 RELOAD
、SHUTDOWN
和 SYSTEM_VARIABLES_ADMIN
。管理操作是对服务器本身的操作,而不是特定于数据库的操作,因此没有理由在其他授权表中列出这些权限。因此,服务器只需查阅 user
和 global_grants
表即可确定用户是否可以执行管理操作。
FILE
权限也仅在 user
表中指定。它本身不是管理权限,但用户在服务器主机上读取或写入文件的能力与正在访问的数据库无关。
为了允许对 MySQL 授权表执行并发 DML 和 DDL 操作,先前在 MySQL 授权表上获取行锁的读取操作将作为非锁定读取执行。作为非锁定读取在 MySQL 授权表上执行的操作包括:
SELECT
语句和其他只读语句,它们通过连接列表和子查询从授权表读取数据,包括SELECT ... FOR SHARE
语句,使用任何事务隔离级别。使用任何事务隔离级别,从授权表(通过连接列表或子查询)读取数据但不修改它们的数据操作语言 (DML) 操作。
如果在使用基于语句的复制时执行从授权表读取数据时不再获取行锁的语句,则会报告警告。
使用 -binlog_format=mixed
时,从授权表读取数据的 DML 操作将作为行事件写入二进制日志,以使操作对混合模式复制安全。
从授权表读取数据的 SELECT ... FOR SHARE
语句会报告警告。对于 FOR SHARE
子句,授权表不支持读取锁。
使用 SERIALIZABLE
隔离级别执行并从授权表读取数据的 DML 操作会报告警告。授权表不支持使用 SERIALIZABLE
隔离级别时通常会获取的读取锁。