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.4 中,授权表使用 InnoDB
存储引擎,并且是事务性的。在 MySQL 8.4 之前,授权表使用 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 内置身份验证插件(已弃用的 mysql_native_password
、已弃用的 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 类型权限列值
表名 | 列名 | 可能的集合元素 |
---|---|---|
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
隔离级别时通常会获取的读取锁。