授权访问
指定帐户名称
MySQL帐户名由用户名和主机名组成,这可以为具有相同用户名且可以从不同主机进行连接的用户创建不同的帐户。本节介绍如何编写帐户名称,包括特殊值和通配符规则。
MySQL角色名称与帐户名称相似,但在“指定角色名称”中有所描述。
在SQL语句,例如CREATE USER
,GRANT
和SET PASSWORD
帐户名遵循以下规则:
- 帐户名称的语法为。
'user_name'@'host_name'
- 仅包含用户名的帐户名等效于。例如,等效于。
'user_name'@'%'
'me'
'me'@'%'
- 如果用户名和主机名是合法的,则不需要用引号引起来。必须使用引号来指定
user_name
包含特殊字符的字符串(例如空格或-
),或host_name
包含特殊字符或通配符的字符串(例如.
或%
)。例如,在帐户名中'test-user'@'%.com'
,用户名和主机名部分都需要加引号。 - 使用反引号(
`
),单引号('
)或双引号("
)将用户名和主机名作为标识符或字符串引号。有关字符串引用和标识符引用的准则,请参见“字符串文字”和“架构对象名称”。 - 用户名和主机名部分(如果带引号)必须分开引号。也就是说
'me'@'localhost'
,不要写'me@localhost'
。后者实际上等效于'me@localhost'@'%'
。 - 对
CURRENT_USER
orCURRENT_USER()
函数的引用等效于从字面上指定当前客户端的用户名和主机名。
MySQL mysql
使用用户名和主机名部分的单独列将帐户名存储在系统数据库的授权表中:
- 该
user
表为每个帐户包含一行。该User
和Host
列存储用户名和主机名。该表还指示该帐户具有哪些全局特权。 - 其他授权表指示帐户具有数据库和数据库中对象的特权。这些表具有
User
和Host
列来存储帐户名。这些表中的每一行都与user
表中具有相同User
和Host
值的帐户相关联。 - 为了进行访问检查,用户值的比较区分大小写。主机值的比较不区分大小写。
有关授权表中存储的用户名和主机名属性的其他详细信息,例如最大长度,请参阅“授权表作用域列属性”。
用户名和主机名具有某些特殊值或通配符约定,如下所述。
帐户名的用户名部分可以是与输入连接尝试的用户名在字面上匹配的非空值,也可以是与任何用户名匹配的空值(空字符串)。用户名为空的帐户是匿名用户。要在SQL语句中指定匿名用户,请使用带引号的空用户名部分,例如''@'localhost'
。
帐户名的主机名部分可以采用多种形式,并且可以使用通配符:
- 主机值可以是主机名或IP地址(IPv4或IPv6)。该名称
'localhost'
表示本地主机。IP地址'127.0.0.1'
表示IPv4环回接口。IP地址'::1'
表示IPv6环回接口。 主机名或IP地址值中允许使用
%
和_
通配符。这些具有与操作员执行的模式匹配操作相同的含义LIKE
。例如,主机值'%'
匹配任何主机名,而值'%.mysql.com'
匹配mysql.com
域中的任何主机。'198.51.100.%'
匹配198.51.100 C类网络中的任何主机。由于主机值中允许使用IP通配符值(例如,
'198.51.100.%'
以匹配子网上的每个主机),因此有人可以尝试通过命名host来利用此功能198.51.100.somewhere.com
。为了阻止此类尝试,MySQL不会对以数字和点开头的主机名执行匹配。例如,如果主机名为1.2.example.com
,则其名称将永远与帐户名的主机部分不匹配。IP通配符值只能匹配IP地址,不能匹配主机名。对于指定为IPv4地址的主机值,可以给出网络掩码以指示要用于网络号的地址位。网络掩码符号不能用于IPv6地址。
语法为。例如:
host_ip/netmask
CREATE USER 'david'@'198.51.100.0/255.255.255.0';这使得
david
可以从具有满足client_ip
以下条件的IP地址的任何客户端主机进行连接:client_ip & netmask = host_ip
也就是说,对于
CREATE USER
刚刚显示的语句:client_ip & 255.255.255.0 = 198.51.100.0
满足此条件的IP地址范围为
198.51.100.0
到198.51.100.255
。网络掩码通常以设置为1的位开始,然后是设置为0的位。
198.0.0.0/255.0.0.0
:198类A网络上的任何主机198.51.100.0/255.255.0.0
:198.51 B类网络上的任何主机198.51.100.0/255.255.255.0
:198.51.100 C类网络上的任何主机198.51.100.1
:仅具有此特定IP地址的主机
服务器使用系统DNS解析程序返回的客户端主机名或IP地址的值,将帐户名中的主机值与客户端主机进行匹配。除了使用网络掩码表示法指定帐户主机值的情况之外,服务器甚至会以字符串匹配的形式执行此比较,即使是将帐户主机值指定为IP地址也是如此。这意味着您应该以DNS使用的相同格式指定帐户主机值。以下是需要注意的问题示例:
- 假设本地网络上的主机的标准名称为
host1.example.com
。如果DNS返回此主机的名称查询为host1.example.com
,请在帐户主机值中使用该名称。如果DNS仅返回host1
,请host1
改用。 - 如果DNS返回的给定主机的IP地址为
198.51.100.2
,则将匹配帐户主机值,198.51.100.2
但不匹配198.051.100.2
。同样,它将匹配198.51.100.%
而不是的帐户托管模式198.051.100.%
。
为避免此类问题,建议检查DNS返回主机名和地址的格式。在MySQL帐户名称中使用相同格式的值。
指定角色名称
MySQL角色名称是指角色,这些角色被称为特权集合。有关角色用法的示例,
角色名称的语法和语义类似于帐户名称;请参见“创建账户”。与存储在授权表中一样,它们具有与帐户名称相同的属性,这在“授权表范围列属性”中进行了描述。
角色名称在以下方面与帐户名称不同:
- 角色名称的用户部分不能为空。因此,不存在“匿名角色”类似的概念“匿名用户。”
- 至于帐户名称,省略角色名称的主机部分将导致的主机部分
'%'
。但是与'%'
帐户名称不同,'%'
角色名称的主机部分没有通配符属性。例如,对于'me'@'%'
用作角色名称的名称,主机部分('%'
)只是一个文字值;它没有“ ny host ”匹配属性。 - 角色名称的主机部分中的网络掩码表示法没有任何意义。
- 帐户名可以
CURRENT_USER()
在多个上下文中使用。角色名称不是。
mysql.user
系统表中的一行有可能同时充当帐户和角色。在这种情况下,任何特殊的用户名或主机名匹配属性都不适用于名称用作角色名称的上下文。例如,您无法执行以下语句,而期望该语句将使用具有用户部分myrole
和任何主机名的所有角色来设置当前会话角色:
SET ROLE 'myrole'@'%';
相反,该语句将会话的活动角色设置为名称完全相同的角色'myrole'@'%'
。
因此,通常仅使用用户名部分指定角色名称,而将主机名部分隐式设置为'%'
。'%'
如果要创建一个既可以用作角色又可以用作从给定主机进行连接的用户帐户的名称,则指定具有非主机部分的角色将很有用。
访问控制,阶段1:连接验证
当您尝试连接到MySQL服务器时,服务器根据以下条件接受或拒绝连接:
- 您的身份以及是否可以通过提供正确的密码来验证您的身份
- 您的帐户是锁定还是未锁定
服务器首先检查凭据,然后检查帐户锁定状态。任一步骤失败都会导致服务器完全拒绝您的访问。否则,服务器将接受连接,然后进入阶段2并等待请求。
使用三个执行凭证检查user
表范围列(Host
,User
,和 uthentication_string
)。锁定状态记录在user
表 ccount_locked
列中。服务器仅在某些表行中的Host
和User
列user
与客户端主机名和用户名匹配,客户端提供该行中指定的密码且 ccount_locked
值为时才接受连接'N'
。有关允许Host
和User
值的规则,请参见“账户权限控制”。可以使用该ALTER USER
语句更改帐户锁定。
您的身份基于以下两条信息:
- 您连接的客户端主机
- 您的MySQL用户名
如果User
列值为非空白,则传入连接中的用户名必须完全匹配。如果该User
值为空白,则它与任何用户名匹配。如果user
与传入连接匹配的表行的用户名为空,则该用户将被视为没有名称的匿名用户,而不是具有客户端实际指定名称的用户。这意味着在连接持续时间内(即在第2阶段),将使用空白用户名进行所有进一步的访问检查。
该 uthentication_string
列可以为空白。这不是通配符,并不意味着任何密码都匹配。这意味着用户必须在未指定密码的情况下进行连接。如果服务器使用插件对客户端进行身份验证,则该插件实现的身份验证方法可以使用也可以不使用该 uthentication_string
列中的密码。在这种情况下,有可能还使用外部密码对MySQL服务器进行身份验证。
表中的非空白 uthentication_string
值user
表示加密的密码。MySQL不会将密码存储为明文,任何人都可以看到。而是,将对尝试连接的用户提供的密码进行加密(使用由帐户身份验证插件实现的密码哈希方法)。然后在检查密码是否正确时在连接过程中使用加密的密码。这样就不会在连接上传输加密密码。请参见“账户权限控制”。
从MySQL的角度来看,加密密码是真实密码,因此您绝不应该允许任何人访问它。特别是,请勿授予非管理用户对mysql
系统数据库中的表的读取权限。
下表显示了如何将各种的组合User
和Host
值在user
表适用于传入的连接。
User 值 | Host 值 | 允许的连接 |
---|---|---|
'fred' | 'h1.example.net' | fred ,来自h1.example.net |
'' | 'h1.example.net' | 任何用户,从h1.example.net |
'fred' | '%' | fred ,从任何主机连接 |
'' | '%' | 从任何主机连接的任何用户 |
'fred' | '%.example.net' | fred ,从example.net 域中的任何主机进行连接 |
'fred' | 'x.example.%' | fred ,从连接x.example.net ,x.example.com ,x.example.edu ,等;这可能没有用 |
'fred' | '198.51.100.177' | fred ,从具有IP地址的主机连接198.51.100.177 |
'fred' | '198.51.100.%' | fred ,从198.51.100 C类子网中的任何主机进行连接 |
'fred' | '198.51.100.0/255.255.255.0' | 与前面的示例相同 |
传入连接的客户端主机名和用户名可能与user
表中的多行匹配。前面的示例集对此进行了演示:显示的多个条目与h1.example.net
by 的连接匹配fred
。
当可能有多个匹配项时,服务器必须确定要使用哪个匹配项。它可以解决此问题,如下所示:
- 每当服务器将
user
表读入内存时,它就会对行进行排序。 - 当客户端尝试连接时,服务器将按排序顺序浏览各行。
- 服务器使用与客户端主机名和用户名匹配的第一行。
服务器使用排序规则,该规则首先对具有最特定Host
值的行进行排序。文字主机名和IP地址是最具体的。(原义IP地址的特异性不受其是否具有网络掩码的影响,因此,198.51.100.13
并且198.51.100.0/255.255.255.0
被认为是同等具体的。)该模式'%'
表示“任何主机”,并且是最不具体的。空字符串''
也表示“任何主机”,但排在后面'%'
。具有相同Host
值的行User
首先按最特定的值排序(空白User
值表示“任何用户”并且是最不明确的)。对于具有相同特定值Host
和User
值的行,顺序是不确定的。
要参见其工作原理,假设user
表如下所示:
+----------- +---------- +- | Host | User | ... +----------- +---------- +- | % | root | ... | % | jeffrey | ... | localhost | root | ... | localhost | | ... +----------- +---------- +-
当服务器将表读入内存时,它将使用刚刚描述的规则对行进行排序。排序后的结果如下所示:
+----------- +---------- +- | Host | User | ... +----------- +---------- +- | localhost | root | ... | localhost | | ... | % | jeffrey | ... | % | root | ... +----------- +---------- +-
当客户端尝试连接时,服务器将浏览已排序的行并使用找到的第一个匹配项。对于从连接localhost
由jeffrey
两个从表匹配的行的:一个具有Host
和User
的值'localhost'
和''
,和一个具有值'%'
和'jeffrey'
。该'localhost'
行首先以排序顺序显示,因此这是服务器使用的行。
这是另一个例子。假设user
表如下所示:
+---------------- +---------- +- | Host | User | ... +---------------- +---------- +- | % | jeffrey | ... | h1.example.net | | ... +---------------- +---------- +-
排序的表如下所示:
+---------------- +---------- +- | Host | User | ... +---------------- +---------- +- | h1.example.net | | ... | % | jeffrey | ... +---------------- +---------- +-
通过连接jeffrey
从h1.example.net
由第一行匹配,而通过连接jeffrey
来自任何主机通过第二匹配。
注意常见的误解是认为,对于给定的用户名,当服务器尝试查找连接的匹配项时,首先使用显式命名该用户的所有行。这不是真的。前面的例子中示出了这一点,其中从连接
h1.example.net
通过jeffrey
首先由包含行匹配不'jeffrey'
作为User
列值,而是由没有用户名的行。结果,jeffrey
即使他在连接时指定了用户名,也被认证为匿名用户。
如果您能够连接到服务器,但是特权不是您所期望的,则您可能已通过其他帐户的身份验证。要查找服务器用来验证您身份的帐户,请使用CURRENT_USER()
函数。(请参见“信息函数”。)它以格式表示一个值,该值指示匹配表行中的和值。假设连接并发出以下查询:user_name@host_name
User
Host
user
jeffrey
mysql>SELECT CURRENT_USER(); +---------------- + | CURRENT_USER() | +---------------- + | @localhost | +---------------- +
此处显示的结果表明匹配user
表行的User
列值为空。换句话说,服务器将其jeffrey
视为匿名用户。
诊断身份验证问题的另一种方法是打印出user
表格并手动对其进行排序,以参见进行第一个匹配的位置。
访问控制,阶段2:请求验证
建立连接后,服务器进入访问控制的第二阶段。对于通过该连接发出的每个请求,服务器都会确定要执行的操作,然后检查您是否具有足够的特权。这是授予表中的特权列起作用的地方。这些权限可以来自任何的user
,global_grants
,db
,tables_priv
,columns_priv
,或procs_priv
表。(您可能会发现参考“赠款表”很有帮助,该表列出了每个赠款表中的列。)
在user
和global_grants
表授予全局权限。这些表中给定帐户的行表示无论默认数据库是什么,在全局基础上应用的帐户特权。例如,如果user
表授予您DELETE
特权,则可以从服务器主机上任何数据库中的任何表中删除行。明智的做法是user
只向需要特权的人(例如数据库管理员)授予表中的特权。对于其他用户,请将user
表中的所有特权设置为'N'
并仅在更特定的级别(对于特定的数据库,表,列或例程)授予特权。也可以全局授予数据库特权,但可以使用部分吊销来限制它们在特定数据库上的行使(请参见“使用部分吊销的特权限制”)。
该db
表授予特定于数据库的特权。该表的范围列中的值可以采用以下形式:
- 空白
User
值与匿名用户匹配。非空值从字面上匹配;用户名中没有通配符。 - 通配符
%
和_
可以在Host
和Db
列中使用。这些具有与操作员执行的模式匹配操作相同的含义LIKE
。如果要在授予特权时按字面使用任何一个字符,则必须使用反斜杠将其转义。例如,要将下划线字符(_
)作为数据库名称的一部分,请\_
在GRANT
语句中指定它。 '%'
或空白Host
值的装置“的任何主机。”- 一个
'%'
或空白Db
值的手段“任何数据库。”
服务器将db
表读入内存,并在读取user
表的同时对其进行排序。服务器排序db
基于表Host
,Db
和User
范围列。与user
表一样,排序将最具体的值放在最前面,最不具体的值放在最后,当服务器寻找匹配的行时,它将使用找到的第一个匹配项。
tables_priv
,columns_priv
和procs_priv
表授予表专用,列具体和特定例行特权。这些表的作用域列中的值可以采用以下形式:
- 通配符
%
和_
可以在Host
列中使用。这些具有与操作员执行的模式匹配操作相同的含义LIKE
。 '%'
或空白Host
值的装置“的任何主机。”- 在
Db
,Table_name
,Column_name
,和Routine_name
列不能包含通配符或空白。
服务器排序tables_priv
,columns_priv
以及procs_priv
基于表Host
,Db
和User
列。这类似于db
表排序,但是更简单,因为只有Host
列可以包含通配符。
服务器使用排序的表来验证它收到的每个请求。对于需要管理特权(例如SHUTDOWN
或RELOAD
)的请求,服务器仅检查user
和global_privilege
表,因为这是唯一指定管理特权的表。如果这些表中帐户的行允许请求的操作,则服务器将授予访问权限,否则拒绝访问。例如,如果要执行mysqladmin shutdown,但user
表行未授予SHUTDOWN
您特权,则服务器甚至不检查db
表就拒绝访问。(后面的表中没有Shutdown_priv
列,因此无需检查。)
对于与数据库相关的请求(INSERT
,UPDATE
等等),服务器首先检查user
表行中用户的全局特权(减去部分撤销所施加的任何特权限制)。如果该行允许请求的操作,则授予访问权限。如果user
表中的全局特权不足,则服务器从表中确定用户的特定于数据库的特权db
:
该服务器会在db
表上的匹配Host
,Db
以及User
列。在Host
与User
列对应连接用户的主机名和MySQL用户名。该Db
列与用户要访问的数据库匹配。如果Host
nd的行都没有User
,则拒绝访问。
确定db
表行授予的特定于数据库的特权后,服务器会将它们添加到user
表授予的全局特权中。如果结果允许请求的操作,则授予访问权限。否则,服务器将依次检查tables_priv
和columns_priv
表中用户的表特权和列特权,将这些特权添加到用户特权中,并根据结果允许或拒绝访问。对于存储例程操作,服务器使用procs_priv
表而不是tables_priv
和columns_priv
。
用布尔值表示,前面关于如何计算用户特权的描述可以总结如下:
global privileges OR (database privileges ANDhost privileges ) ORtable privileges ORcolumn privileges ORroutine privileges
可能不清楚,为什么,如果最初发现全局特权不足以执行请求的操作,则服务器随后会将这些特权添加到数据库,表和列特权中。原因是请求可能需要一种以上的特权。例如,如果执行一条INSERT INTO ... SELECT
语句,则需要INSERT
和SELECT
特权。您的特权可能是这样的:user
表行向全局授予一个特权,而db
表行授予另一个专门用于相关数据库的行。在这种情况下,您具有执行请求所必需的特权,但是服务器无法仅从全局特权或数据库特权中分辨出这一点。它必须根据组合的权限做出访问控制决定。