参考官方文档:
https://dev.mysql.com/doc/refman/5.7/en/privilege-system.html
MySQL权限系统的主要功能是对从给定主机连接的用户进行身份验证,并将该用户与数据库上的权限(如SELECT,INSERT,UPDATE和DELETE)相关联。 其他功能包括具有匿名用户和授予特定于MySQL的功能(如LOAD DATA INFILE和管理操作)的权限。
有些事情你不能用MySQL权限系统来做:
- 您不能明确指定给定用户应该被拒绝访问。 也就是说,你不能明确地匹配用户,然后拒绝连接。
- 您不能指定用户有权创建或删除数据库中的表,但不能创建或删除数据库本身
- 密码适用于全局帐户。 您不能将密码与特定对象(如数据库,表格或routine)相关联。
MySQL特权系统的用户界面由SQL语句组成,如CREATE USER,GRANT和REVOKE。
在内部,服务器将权限信息存储在mysql数据库的授权表中(也就是在名为mysql的数据库中)。 MySQL服务器在启动时将这些表的内容读取到内存中,并基于授予表的内存副本对访问控制做出决定。
MySQL特权系统确保所有用户只能执行允许的操作。 作为用户,当您连接到MySQL服务器时,您的身份由连接的主机和您指定的用户名确定。 在连接后发出请求时,系统将根据您的身份和您想要执行的操作授予权限。
因为没有理由认为给定的用户名属于所有主机上的同一个人,所以MySQL会将您的主机名和用户名都视为识别您的身份。 例如,从office.example.com连接的用户joe不是与从home.example.com连接的用户joe是同一个人。 MySQL通过使您能够区分具有相同名称的不同主机上的用户来处理这个问题:您可以授予来自office.example.com的joe连接的一组权限,以及来自home的joe连接的另一组权限.example.com的。 要查看给定帐户具有的权限,请使用SHOW GRANTS语句。 例如:
SHOW GRANTS FOR ‘joe’@’office.example.com‘;
SHOW GRANTS FOR ‘joe’@’home.example.com‘;
当您运行连接到服务器的客户端程序时,MySQL访问控制涉及两个阶段:
阶段1:服务器根据您的身份接受或拒绝连接,以及您是否通过提供正确的密码来验证您的身份。
阶段2:假设您可以连接,服务器会检查您发出的每条语句,以确定您是否有足够的权限来执行它。 例如,如果尝试从数据库中的表中选择行或从数据库中删除表,服务器会验证您是否具有该表的SELECT权限或数据库的DROP权限。
如果您在连接时更改了权限(通过您自己或其他人),那么对于您发布的下一条语句,这些更改不一定会立即生效。
6.2.1 MYSQL 提供的的权限
授予MySQL帐户的权限决定了用户可以执行的操作。 MySQL权限在应用的上下文和不同的操作级别上有所不同:
- 管理权限使用户能够管理MySQL服务器的操作。 这些权限是全局性的,因为它们不是特定于特定数据库的。
- 数据库特权适用于数据库及其中的所有对象。 这些特权可以授予特定数据库,也可以授予全局特权,以便适用于所有数据库。
- 可为数据库中的特定对象,数据库内给定类型的所有对象(例如数据库中的所有表)授予数据库对象(如表,索引,视图和存储例程)的权限。
有关帐户权限的信息存储在mysql系统数据库中的user,db,tables_priv,columns_priv和procs_priv表中(请参见第6.2.2节“授予表”)。 MySQL服务器在启动时将这些表的内容读入内存,并在第6.2.6节“特权更改生效时”中指出的情况下重新加载它们。 访问控制决策基于授权表的内存中副本。
一些MySQL版本引入了对授权表结构的更改以添加新的特权或功能。 为了确保您可以利用任何新功能,请更新您的授权表,以便在升级MySQL时拥有当前结构。
以下列表提供了MySQL中可用权限的一般描述。 特定的SQL语句可能具有比此处指示的更具体的特权要求。 如果是这样,那么有关声明的描述将提供详细信息。
- ALL或ALL PRIVILEGES特权说明符是简写。 它代表“在给定特权级别提供的所有特权”(GRANT OPTION除外)。 例如,在全局或表级授予ALL将授予所有全局特权或所有表级特权。
- ALTER特权支持使用ALTER TABLE语句来更改表的结构。 ALTER TABLE还需要CREATE和INSERT权限。 重命名表需要旧表上的ALTER和DROP,新表上的CREATE和INSERT。
- ALTER ROUTINE权限需要修改或删除存储的routines (过程和函数)。
- CREATE特权可以创建新的数据库和表。
- 创建存储routines(过程和函数)需要CREATE ROUTINE特权。
- 需要CREATE TABLESPACE权限才能创建,更改或删除表空间和日志文件组。
- CREATE TEMPORARY TABLES特权允许使用CREATE TEMPORARY TABLE语句创建临时表。
会话创建临时表后,服务器不会在该表上执行进一步的特权检查。 创建会话可以对表执行任何操作,例如DROP TABLE,INSERT,UPDATE或SELECT。
- CREATE USER权限允许使用ALTER USER,CREATE USER,DROP USER,RENAME USER和REVOKE ALL PRIVILEGES语句。
- CREATE VIEW特权支持使用CREATE VIEW语句。
- DELETE权限使行可以从数据库中的表中删除。
- DROP特权使您能够删除(删除)现有数据库,表和视图。 有DROP权限,才能在分区表上使用语句ALTER TABLE … DROP PARTITION。 TRUNCATE TABLE也需要DROP权限。 如果您将MySQL数据库的DROP权限授予给用户,则该用户可以删除存储MySQL访问权限的数据库。
- EVENT权限是创建,更改,删除或查看Event Scheduler的事件所必需的。
- EXECUTE特权是执行存储例程(过程和函数)所必需的。
- FILE权限使您可以使用LOAD DATA INFILE和SELECT … INTO OUTFILE语句以及LOAD_FILE()函数来读取和写入服务器主机上的文件。 具有FILE特权的用户可以读取服务器主机上任何可读或MySQL服务器可读的文件。 (这意味着用户可以读取任何数据库目录中的任何文件,因为服务器可以访问这些文件中的任何文件。)FILE特权还使用户能够在MySQL服务器具有写入权限的任何目录中创建新文件。 这包括服务器的数据目录,其中包含实现权限表的文件。 作为安全措施,服务器不会覆盖现有文件。 从MySQL 5.7.17开始,FILE特权需要为CREATE TABLE语句使用DATA DIRECTORY或INDEX DIRECTORY表选项。
要限制可以读取和写入文件的位置,请将secure_file_priv系统设置为特定的目录。
- 通过 GRANT OPTION,您可以授予其他用户或从其他用户中删除您自己拥有的特权。
- INDEX特权使您能够创建或drop(remove)索引。 INDEX适用于现有的表格。 如果您具有表的CREATE特权,则可以在CREATE TABLE语句中包含索引定义。
- INSERT特权允许将行插入数据库的表中。 INSERT对于ANALYZE TABLE,OPTIMIZE TABLE和REPAIR TABLE表维护语句也是必需的。
- LOCK TABLES特权允许使用明确的LOCK TABLES语句来锁定您拥有SELECT特权的表。 这包括使用写入锁,这会阻止其他会话读取锁定的表。
- PROCESS特权适用于显示有关在服务器内执行的线程的信息(即关于正在由会话执行的语句的信息)。 该特权可以使用SHOW PROCESSLIST或mysqladmin processlist查看属于其他帐户的线程; 你总是可以看到你自己的线程。 PROCESS特权还允许使用SHOW ENGINE。
- PROXY特权使用户能够冒充或成为另一个用户。
- 创建外键约束需要父表的REFERENCES特权。
- RELOAD权限允许使用FLUSH语句。 它还启用与FLUSH操作等效的mysqladmin命令:flush-hosts,flush-logs,flush-privileges,flush-status,flush-tables,flush-threads,refresh和reload。
reload命令会通知服务器将授权表重新加载到内存中。 flush-privileges是reload的同义词。 刷新命令关闭并重新打开日志文件并刷新所有表。 其他flush-xxx命令执行类似于刷新的功能,但是更具体并且在某些情况下可能是优选的。 例如,如果只想刷新日志文件,flush-logs是比刷新更好的选择。
- REPLICATION CLIENT权限允许使用SHOW MASTER STATUS,SHOW SLAVE STATUS和SHOW BINARY LOGS语句。
- REPLICATION SLAVE权限应授予从服务器用作主服务器连接到当前服务器的帐户。 如果没有此权限,从属服务器无法请求对主服务器上的数据库进行更新。
- SELECT特权使您能够从数据库中的表中选择行。 SELECT语句只有在它们实际从表中检索行时才需要SELECT特权。 某些SELECT语句不访问表,并且可以在没有任何数据库权限的情况下执行。 例如,您可以使用SELECT作为简单计算器来计算不引用表的表达式:
SELECT 1+1;
SELECT PI()*2;
其他读取列值的语句也需要SELECT权限。 例如,对于在UPDATE语句中col_name = expr赋值的右侧引用的列或DELETE或UPDATE语句的WHERE子句中命名的列,需要SELECT。
(delete,update 其实也是要先检索数据的)
用于EXPLAIN的表或视图也需要SELECT特权,包括任何基础的视图表。
- SHOW DATABASES权限使帐户能够通过发出SHOW DATABASE语句来查看数据库名称。 没有此权限的帐户只能看到他们具有某些权限的数据库,并且如果服务器是使用–skip-show-database选项启动的,则完全不能使用该语句。 请注意,任何全局特权都是数据库的特权。
- SHOW VIEW特权支持使用SHOW CREATE VIEW语句。 EXPLAIN使用的视图也需要此权限。
- SHUTDOWN特权支持使用SHUTDOWN语句,mysqladmin shutdown命令和mysql_shutdown()C API函数。
- SUPER权限启用这些操作和服务器行为:
- 通过修改全局系统变量来启用配置更改。 对于某些系统变量,设置会话值还需要SUPER权限; 如果是这样,则在变量描述中指出。 示例包括binlog_format,sql_log_bin和sql_log_off。
- 启用对全局事务特性的更改
- 启用从属服务器上的启动和停止复制,包括Group复制。
- 允许使用CHANGE MASTER TO和CHANGE REPLICATION FILTER语句。
- 通过PURGE BINARY LOGS和BINLOG语句启用二进制日志控制。
- 在执行视图或存储程序时启用设置有效授权ID。 具有此权限的用户可以在视图或存储程序的DEFINER属性中指定任何帐户。
- 允许使用CREATE SERVER,ALTER SERVER和DROP SERVER语句。
- 启用mysqladmin调试命令。
- 启用InnoDB key旋转。
- 通过DES_ENCRYPT()函数启用读取DES密钥文件。
- 启用版本令牌用户定义函数的执行。
- 启用对非SUPER帐户不允许的客户端连接的控制:
- 启用使用KILL语句或mysqladmin kill命令来终止属于其他帐户的线程。 (你总是可以杀死你自己的线程。)
- 即使达到由max_connections系统变量控制的连接限制,服务器也会接受来自SUPER客户端的一个连接。
- 即使启用了read_only系统变量,也可以执行更新。 这适用于表更新和使用帐户管理语句,如GRANT和REVOKE。
- SUPER客户端连接时,服务器不执行init_connect系统变量内容。
- 处于脱机模式(已启用offline_mode)的服务器不会在下一个客户端请求中终止SUPER客户端连接,并接受来自SUPER客户端的新连接。
如上所述,如果启用二进制日志记录,则可能还需要SUPER权限才能创建或更改存储的功能
- TRIGGER特权启用触发器操作。 您必须拥有该表的特权才能创建,删除,执行或显示该表的触发器。
当激活触发器时(由有权为与触发器关联的表执行INSERT,UPDATE或DELETE语句的用户),触发器执行要求定义触发器的用户仍具有TRIGGER特权。
- UPDATE特权使行能够在数据库中的表中更新。
- USAGE特权说明符代表“无特权”。它在GRANT的全局级别上用于修改帐户属性,例如资源限制或SSL特性,而无需命名特定的帐户特权。 SHOW GRANTS显示USAGE以指示帐户在特权级别没有权限。
向账户授予只有它需要的权限是一个好主意。 您在授予FILE和管理权限时应特别小心:
- 滥用FILE特权将任何文件读入数据库表,MySQL服务器可以在服务器主机上读取任何文件。 这包括服务器数据目录中所有世界可读的文件和文件。 然后可以使用SELECT访问该表以将其内容传输到客户端主机。
- GRANT OPTION特权使用户可以将其权限授予其他用户。 具有不同特权和GRANT OPTION特权的两个用户可以组合特权。
- ALTER特权可以用来通过重命名表来推翻特权系统。
- SHUTDOWN权限可以被滥用以完全通过终止服务器来拒绝对其他用户的服务。
- PROCESS特权可用于查看当前正在执行的语句的纯文本,包括设置或更改密码的语句。
- SUPER权限可用于终止其他会话或更改服务器的运行方式。
- 授予mysql数据库本身的权限可用于更改密码和其他访问权限信息。 密码被加密存储,所以恶意用户不能简单地阅读它们以知道明文密码。 但是,具有对用户表authentication_string列的写入权限的用户可以更改帐户的密码,然后使用该帐户连接到MySQL服务器。
6.2.2 Grant 表
MySQL系统数据库包含多个授权表,其中包含有关用户帐户及其所拥有的权限的信息。 本节介绍这些表。
要操作授权表的内容,可以使用帐户管理语句(如CREATE USER,GRANT和REVOKE)间接修改它们以设置帐户并控制每个帐户可用的权限。 请参见第13.7.1节“帐户管理语句”。 这里的讨论描述了授权表的底层结构以及服务器在与客户端交互时如何使用其内容。
注意:
使用诸如INSERT,UPDATE或DELETE之类的语句直接修改授权表是不鼓励的,并且风险自负。 服务器可以自由忽略由于此类修改而变得格式错误的行。
从MySQL 5.7.18开始,对于修改授权表的任何操作,服务器会检查该表是否具有预期的结构,如果没有,则会产生错误。 必须运行mysql_upgrade以将表更新为预期的结构。
导致:
因为任何全局特权都被认为是所有数据库的特权,所以任何全局特权都允许用户使用SHOW DATABASES或检查INFORMATION_SCHEMA的SCHEMATA表来查看所有数据库名称。
服务器在访问控制的第一和第二阶段都使用mysql数据库中的user和db表(请参见第6.2节“MySQL Access Privilege System”)。
user表的, plugin ,authentication_string列存储认证插件和凭证信息。
服务器使用帐户行的 plugin 列中指定的插件来验证帐户的连接尝试。
密码过期后可以通过将密码设置为其当前值来“重置”密码。 作为一个良好的政策,最好选择一个不同的密码。
在访问控制的第二阶段,服务器执行请求验证,以确保每个客户端对其发出的每个请求拥有足够的权限。 除了用户和db授权表之外,服务器还可以查询tables_priv和columns_priv表以获取涉及表的请求。 后面的表格在表和列级别提供更好的权限控制。 它们具有下表中显示的列。
要使帐户能够将PROXY特权授予其他帐户,它必须在proxies_priv表中具有一行,并将With_grant设置为1,并将Proxied_host和Proxied_user设置为指示可授予特权的帐户。 例如,在MySQL安装过程中创建的’root’@’localhost’帐户在proxies_priv表中有一行,可以为”@@” 授予PROXY特权,即为所有用户和所有主机授予PROXY特权。 这使得root可以设置代理用户,也可以委派给其他账户设置代理用户的权限。
表6.7 设置类型权限列值
只有用户表指定管理权限,例如RELOAD和SHUTDOWN。 管理操作是服务器本身的操作,并不是特定于数据库的,因此没有理由在其他授权表中列出这些权限。 因此,服务器只需咨询用户表以确定用户是否可以执行管理操作。
FILE权限也仅在用户表中指定。 这不是管理权限,但用户在服务器主机上读写文件的能力与被访问的数据库无关。
服务器在启动时将授权表的内容读入内存。 您可以通过发出FLUSH PRIVILEGES语句或执行mysqladmin flush-privileges或mysqladmin reload命令来通知它重新加载表。
在修改帐户时,验证您的更改是否具有预期效果是一个好主意。 要检查给定帐户的权限,请使用SHOW GRANTS语句。 例如,要确定授予用户名和主机名值为bob和http://pc84.example.com的帐户的权限,请使用以下语句:
SHOW GRANTS FOR ‘bob’@’pc84.example.com’;
要显示帐户的非权限属性,请使用SHOW CREATE USER:
SHOW CREATE USER ‘bob’@’pc84.example.com’;
6.2.3指定帐户名称
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或CURRENT_USER()函数的引用等同于从字面上指定当前客户端的用户名和主机名。
MySQL在mysql 系统数据库授权表中使用单独的列分别存储主机名和用户名:
- user表每一行是一个账户。 user和Host列存储用户名和主机名。 该表还指出该帐户拥有哪些全局特权。
- 其他授权表指示帐户对数据库中的数据库和对象具有的权限。 这些表具有User和Host列来存储帐户名称。 这些表中的每一行都与具有相同用户和主机值的用户表中的帐户相关联。
- 为了进行访问检查,用户值的比较区分大小写。 主机值的比较不区分大小写。
用户名和主机名具有某些特殊值或通配符约定,如下所述。
帐户名称的用户名部分可以是与输入连接尝试的用户名字面相匹配的非空白值,也可以是与任何用户名匹配的空值(空字符串)。 具有空白用户名的帐户是匿名用户。 要在SQL语句中指定一个匿名用户,请使用带引号的空用户名部分,如”@’localhost’。
帐户名称的主机名部分可以采用多种形式,并且允许使用通配符:
- 主机值可以是主机名或IP地址(IPv4或IPv6)。 名称’localhost’表示本地主机。 IP地址“127.0.0.1”表示IPv4环回接口。 IP地址’:: 1’表示IPv6环回接口。
- 主机名或IP地址值中允许使用%和_通配符。 这些与LIKE运算符执行的模式匹配操作具有相同的含义。 例如,“%”的主机值匹配任何主机名,而“%.mysql.com”的值匹配http://mysql.com域中的任何主机。 ‘198.51.100.%’与198.51.100 C类网络中的任何主机相匹配。
由于主机值允许使用IP通配符值(例如,’198.51.100.%’ 匹配子网上的每台主机),所以有人可以尝试通过命名主机http://198.51.100.somewhere.com来利用此功能。 为了阻止这种尝试,MySQL不会在以数字和点开头的主机名上执行匹配。 例如,如果主机名为http://1.2.example.com,则其名称从不匹配帐户名称的主机部分。 IP通配符值只能匹配IP地址,而不能匹配主机名。
- 对于指定为IPv4地址的主机值,可以给出网络掩码以指示要为网络号使用多少个地址位。 网络掩码符号不能用于IPv6地址。
语法是host_ip / netmask。 例如:
CREATE USER ‘david’@’198.51.100.0/255.255.255.0’;
这使得david可以从具有以下条件的IP地址client_ip的任何客户端主机进行连接:
client_ip & netmask = host_ip
满足此条件的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相同格式指定帐户主机值。 以下是需要注意的问题示例:
- 假设本地网络上的主机具有http://host1.example.com的完全限定名称。 如果DNS将此主机的名称查找作为http://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.%的帐户主机模式相匹配。
6.2.4 访问控制,阶段1:连接验证
当您尝试连接到MySQL服务器时,服务器根据这些条件接受或拒绝连接:
- 您的身份以及您是否可以通过提供正确的密码来验证您的身份
- 您的帐户是锁定还是未锁定
服务器首先检查凭证,然后检查帐户锁定状态。 任一步骤失败都会导致服务器完全拒绝您的访问。 否则,服务器接受连接,然后进入第2阶段并等待请求。
使用三个user表作用域列(Host,User和authentication_string)执行凭据检查。 锁定状态记录在用户表account_locked列中。 仅当某用户表行中的主机和用户列与客户机主机名和用户名匹配时,服务器才会接受连接,客户机提供该行中指定的密码,并且account_locked值为’N’。 第6.2.3节“指定帐户名称”给出了允许的主机和用户值的规则。 帐户锁定可以通过ALTER USER语句进行更改。
您的身份基于两部分信息:
- 您连接的客户端主机
- 你的MYSQL 用户名
如果user列值不是空白,则传入连接中的用户名必须完全匹配。 如果用户值为空白,则它匹配任何用户名称。 如果user表行空用户名与传入的连接匹配,则该用户被认为是不具有名称的匿名用户,而不是客户端实际指定名称的用户。 这意味着在连接期间(即在第2阶段),所有进一步访问检查都会使用空白用户名。
authentication_string列可以是空白的。 这不是通配符,并不意味着任何密码匹配。 这意味着用户必须连接而不指定密码。 如果服务器使用插件对客户端进行身份验证,则插件实现的身份验证方法可能会或可能不会使用authentication_string列中的密码。 在这种情况下,可能还会使用外部密码对MySQL服务器进行身份验证。
user表中非空白的authentication_string值表示加密的密码。 MySQL不以明文形式存储任何人都能看到的密码。 相反,试图连接的用户提供的密码是加密的(使用由帐户认证插件实施的密码散列方法)。 然后在连接过程中使用加密的密码来检查密码是否正确。 这是在没有经过连接的加密密码的情况下完成的。
从MySQL的角度来看,加密的密码是真正的密码,所以你绝对不应该让任何人访问它。 特别是,不要让非管理员用户读取mysql数据库中的表。
当多个匹配成为可能时,服务器必须确定要使用哪一个。 它解决了这个问题如下:
- 只要服务器将user表读入内存,它就对行进行排序。
- 当客户端尝试连接时,服务器按排序顺序查看行。
- 服务器使用与客户端主机名和用户名匹配的第一行。
服务器使用排序规则,排序规则先排列具有最特定主机值的行。 文字主机名和IP地址是最具体的。 (文字IP地址的特异性不受其是否具有网络掩码的影响,因此198.51.100.13和198.51.100.0/255.255.255.0被视为同等具体。)模式’%’表示“任何主机”,并且是最不具体的。 空字符串 ”也意味着任何主机,但在’%’之后排序。 具有相同主机值的行首先与最具体的用户值排序(空白user值意味着“任何用户”,并且是最不特定的)。 对于具有相同特定主机和用户值的行,该顺序是不确定的。
这是另一个例子。 假设用户表如下所示:
+—————-+———-+-
| Host | User | …
+—————-+———-+-
| % | jeffrey | …
| http://h1.example.net | | …
+—————-+———-+-
被排序的表看起来这样:
+—————-+———-+-
| Host | User | …
+—————-+———-+-
| http://h1.example.net | | …
| % | jeffrey | …
+—————-+———-+-
来自http://h1.example.net的jeffrey的连接与第一行匹配,而来自任何主机的jeffrey的连接与第二行匹配。
注意:
认为对于给定的用户名,当服务器试图为连接找到匹配时,首先使用明确指定该用户的所有行是一种常见的误解。 这不是真的。 前面的例子说明了这一点,其中来自jeffrey的来自http://h1.example.net的连接首先不与作为用户列值的包含’jeffrey’的行匹配,而是与没有用户名的行匹配。 因此,jeffrey被认证为匿名用户,即使他在连接时指定了用户名。
如果您能够连接到服务器,但您的权限不符合您的期望,那么您可能会被认证为其他帐户。 要找出服务器用于验证您的帐户,请使用CURRENT_USER()函数。 (请参见第12.14节“信息函数”。)它以user_name @ host_name格式返回一个值,该值指示匹配的用户表格行中的用户和主机值。 假设jeffrey连接并发出以下查询:
mysql> SELECT CURRENT_USER();
+—————-+
| CURRENT_USER() |
+—————-+
| @localhost |
+—————-+
此处显示的结果表明匹配的用户表行具有空白的用户列值。 换句话说,服务器将jeffrey视为匿名用户。
诊断身份验证问题的另一种方法是打印出用户表并手动排序以查看第一个匹配的位置。
6.2.5 访问控制,阶段2:请求验证
建立连接后,服务器进入访问控制的第2阶段。 对于通过该连接发出的每个请求,服务器都会确定要执行的操作,然后检查您是否有足够的权限来执行此操作。 这是授予表中的权限列发挥作用的地方。 这些权限可以来自任何user,db,tables_priv,columns_priv或procs_priv表。
user表授予在全局基础上分配给您的权限,并且该权限不管默认数据库是什么。 例如,如果user表授予您DELETE权限,则可以从服务器主机上的任何数据库中的任何表中删除行! 将用户表中的权限仅授予需要它们的人员是明智的,例如数据库管理员。 对于其他用户,您应该将user表中的所有权限设置为’N’,并且仅在更具体的级别授予权限。 您可以授予特定数据库,表,列或例程的权限。
db表授予数据库特定的权限。
服务器将db表读入内存,并在读取用户表的同时对其进行排序。 服务器根据Host,Db和User范围列对db表进行排序。 与用户表一样,排序会将最具体的值放在最前面,将最不特定的值放到最后,当服务器查找匹配的行时,会使用找到的第一个匹配项。
tables_priv,columns_priv和procs_priv表授予特定于表,列特定和特定于例程的特权。 这些表的范围列中的值可以采用以下形式:
- Host列中可以使用通配符%和_。 这些与LIKE运算符执行的模式匹配操作具有相同的含义。
- ‘%’或空主机值意味着“任何主机”。
- Db,Table_name,Column_name和Routine_name列不能包含通配符或为空。
服务器根据Host,Db和User列对tables_priv,columns_priv和procs_priv表进行排序。 这与db表排序类似,但更简单,因为只有Host列可以包含通配符。
服务器使用排序表来验证它收到的每个请求。 对于需要管理特权(如SHUTDOWN或RELOAD)的请求,服务器仅检查user表行,因为这是唯一指定管理特权的表。 如果该行允许请求的操作,则服务器授予访问权限,否则拒绝访问。 例如,如果你想执行mysqladmin shutdown,但你的用户表行没有授予SHUTDOWN特权给你,服务器拒绝访问,甚至不检查db表。 (它不包含Shutdown_priv列,所以不需要这样做。)
对于与数据库相关的请求(INSERT,UPDATE等),服务器首先通过查看user行来检查用户的全局特权。 如果该行允许请求的操作,则授予访问权限。 如果用户表中的全局权限不足,则服务器通过检查db表来确定用户的数据库特定权限:
服务器在db表中查找Host,Db和User列上的匹配项。 Host和User列与连接用户的主机名和MySQL用户名匹配。 Db列与User想要访问的数据库相匹配。 如果Host和User没有行,则访问被拒绝。
在确定数据库表行所授予的特定于数据库的特权之后,服务器将它们添加到用户表所授予的全局特权中。 如果结果允许请求的操作,则授予访问权限。 否则,服务器会连续检查tables_priv和columns_priv表中用户的表和列权限,将这些权限添加到用户的权限,并根据结果允许或拒绝访问。 对于存储例程操作,服务器使用procs_priv表而不是tables_priv和columns_priv。
用布尔表达式来表示用户权限计算方式的上述描述可以总结如下:
global privileges
OR (database privileges AND host privileges)
OR table privileges
OR column privileges
OR routine privileges
如果最初发现全局用户行权限不足以满足所请求的操作,则可能不明显的是服务器稍后将这些权限添加到数据库,表和列权限。 原因是请求可能需要多种类型的权限。 例如,如果您执行INSERT INTO … SELECT语句,则需要INSERT和SELECT权限。 您的权限可能是这样的:用户表行授予一个权限,而数据库表行授予另一个权限。 在这种情况下,您拥有执行请求所需的特权,但服务器无法从任何一个表单自行分配; 必须将两个表中的行授予的权限组合在一起。
6.2.6 当特权更改生效时
当mysqld启动时,它将所有授权表内容读入内存。 内存表在此时对访问控制生效。
如果您使用帐户管理语句(如GRANT,REVOKE,SET PASSWORD或RENAME USER)间接修改授权表,则服务器会通知这些更改并立即再次将授权表加载到内存中。
如果直接使用INSERT,UPDATE或DELETE等语句修改授权表,则更改对特权检查没有影响,直到您重新启动服务器或指示它重新加载表。 如果您直接更改授权表,但忘记重新加载它们,则只有在重新启动服务器之后,更改才会生效。 这可能会让你想知道为什么你的改变似乎没有什么区别!
要通知服务器重新加载授权表,请执行刷新特权操作。 这可以通过发出FLUSH PRIVILEGES语句或执行mysqladmin flush-privileges或mysqladmin reload命令来完成。
授权表重新加载会影响每个现有客户端连接的权限,如下所示:
- 表和列权限更改将在客户端的下一个请求中生效。
- 客户端下次执行USE db_name语句时,数据库特权更改将生效。
注意:
客户端应用程序可能缓存数据库名称 因此,如果不实际更改为不同的数据库,这种影响可能对他们不可见。
- 全局权限和密码不受连接客户端的影响。 这些更改仅对后续连接生效。
如果服务器使用–skip-grant-tables选项启动,则不会读取授权表或实施任何访问控制。 任何人都可以连接并做任何事情,这是不安全的。 要使服务器开始读取表并启用访问检查,请刷新权限。
6.2.7 连接到MYSQL 的故障诊断
如果在尝试连接到MySQL服务器时遇到问题,下列各项描述了一些您可以采取的纠正问题的措施。
- 确保服务器正在运行。 如果不是,客户端无法连接到它。 例如,如果尝试连接到服务器失败,并显示如下消息之一,则可能是服务器未运行:
shell> mysql ERROR 2003: Can’t connect to MySQL server on ‘host_name‘ (111) shell> mysql ERROR 2002: Can’t connect to local MySQL server through socket ‘/tmp/mysql.sock’ (111)
- 可能是服务器正在运行,但是您尝试使用与服务器正在侦听的端口不同的TCP / IP端口,命名管道或Unix套接字文件进行连接。 要在调用客户端程序时纠正此问题,请指定–port选项以指示正确的端口号,或指定–socket选项以指示正确的命名管道或Unix套接字文件。 要找出套接字文件的位置,可以使用以下命令:
shell> netstat -ln | grep mysql
- 确保服务器尚未配置为忽略网络连接或(如果您尝试远程连接)尚未将其配置为仅在本地网络接口上侦听。 如果服务器使用–skip-networking启动,则根本不会接受TCP / IP连接。 如果服务器是以–bind-address = 127.0.0.1启动的,它将仅在本地环回接口上侦听TCP / IP连接,并且不会接受远程连接。
- 检查以确保没有防火墙阻止访问MySQL。 您的防火墙可以基于正在执行的应用程序或MySQL用于通信的端口号进行配置(默认为3306)。 在Linux或Unix下,检查您的IP tables(或类似的)配置,以确保端口未被阻塞。 在Windows下,可能需要将ZoneAlarm或Windows防火墙等应用程序配置为不阻止MySQL端口。
- 授予表必须正确设置,以便服务器可以将它们用于访问控制。 对于某些分发类型(例如Windows上的二进制分发版或Linux上的RPM分发版),安装过程初始化MySQL数据目录,包括包含授权表的mysql数据库。 对于不这样做的发行版,您必须手动初始化数据目录。
要确定是否需要初始化授权表,请在数据目录下查找一个mysql目录。 (数据目录通常名为data或var,位于MySQL安装目录下。)确保在mysql数据库目录中有一个名为user.MYD的文件。 如果不是,则初始化数据目录。 这样做并启动服务器后,您应该能够连接到服务器。
- 全新安装后,如果尝试以root身份登录到服务器而不使用密码,则可能会收到以下错误消息。
shell> mysql -u root ERROR 1045 (28000): Access denied for user ‘root’@’localhost’ (using password: NO)
这意味着在安装过程中已经分配了一个root密码,并且必须提供该密码。 请参阅第2.10.4节“保护初始MySQL帐户”,了解密码分配的不同方式,以及在某些情况下如何找到密码。 如果您需要重置根密码,请参阅B.5.3.2节“如何重置根密码”中的说明。 找到或重置密码后,使用–password(或-p)选项以root用户身份重新登录:
但是,如果您已使用mysqld –initialize-insecure初始化MySQL(参见第2.10.1.1节“使用mysqld手动初始化数据目录”获取详细信息),服务器将允许您以root用户身份进行连接,而无需使用密码。 这是一个安全风险,所以你应该为root帐户设置一个密码;
- 如果您已经将现有的MySQL安装更新到更新的版本,您是否运行了mysql_upgrade脚本? 如果没有,那就这样做。 添加新功能时,授权表的结构偶尔会发生更改,因此升级后应始终确保您的表具有当前结构。
- 如果客户端程序在尝试连接时收到以下错误消息,则表示服务器期望密码的格式比客户端能够生成的更新:
shell> mysql Client does not support authentication protocol requested by server; consider upgrading MySQL client
- 请记住,客户端程序会使用选项文件或环境变量中指定的连接参数。 如果客户端程序在命令行上未指定它们时似乎发送了错误的默认连接参数,请检查任何适用的选项文件和您的环境。 例如,如果您在没有任何选项的情况下运行客户端时遇到拒绝访问,请确保您没有在任何选项文件中指定旧密码!
您可以通过使用–no-defaults选项调用客户端程序来禁止使用选项文件。 例如:
shell> mysqladmin –no-defaults -u root version
- 如果出现以下错误,则表示您正在使用不正确的root密码:
shell> mysqladmin -u root -pxxxx ver Access denied for user ‘root’@’localhost’ (using password: YES)
如果即使没有指定密码也会出现上述错误,这意味着您在某些选项文件中列出了不正确的密码。 按照上一项中的描述尝试使用–no-defaults选项。
- localhost是本地主机名的同义词,并且如果不明确指定任何主机,它也是客户端尝试连接的默认主机。
您可以使用–host=127.0.0.1选项来显式命名服务器主机。 这将建立到本地mysqld服务器的TCP / IP连接。 您还可以通过指定使用本地主机的实际主机名的–host选项来使用TCP / IP。 在这种情况下,即使您在与服务器相同的主机上运行客户端程序,也必须在服务器主机上的user表行中指定主机名。
- 访问被拒绝的错误消息会告诉您尝试登录的用户,您尝试连接的客户端主机以及您是否使用密码。 通常,用户表中应该有一行与错误消息中给出的主机名和用户名完全匹配。 例如,如果您收到包含 using password: NO,则表示您尝试登录时没有密码。
- 如果尝试使用mysql -u user_name连接到数据库时发生Access denied错误,则可能是用户表存在问题。 通过执行mysql -u root mysql并发出以下SQL语句来检查:
SELECT * FROM user;
结果应该包含一行,其中包含与您的客户端主机名和您的MySQL用户名匹配的主机和用户列。
- 如果尝试从运行MySQL服务器的主机之外的主机进行连接时发生以下错误,则表示user表中没有与客户端主机匹配的Host值的行:
shell> mysql Host … is not allowed to connect to this MySQL server
您可以通过为尝试连接时使用的客户端主机名和用户名组合设置帐户来解决此问题。
如果您不知道要连接的计算机的IP地址或主机名,则应在用户表中将’%’作为主机列值。 在尝试从客户端机器连接后,使用SELECT USER()查询来查看您真正连接的方式。 然后将用户表行中的’%’更改为日志中显示的实际主机名。 否则,您的系统将处于不安全状态,因为它允许来自任何主机的给定用户名的连接。
在Linux上,可能发生此错误的另一个原因是您正在使用的版本不同的glibc库编译的二进制MySQL版本。 在这种情况下,您应该升级您的操作系统或glibc,或者下载MySQL版本的源代码分发并自行编译。 源RPM通常很容易编译和安装,所以这不是一个大问题。
- 如果您在尝试连接时指定主机名,但在主机名未显示或IP地址时收到错误消息,则意味着MYSQL 在尝试将客户端IP地址解析到名称时出现了错误:
shell> mysqladmin -u root -pxxxx -h some_hostname ver Access denied for user ‘root’@” (using password: YES)
如果您尝试以root身份进行连接并出现以下错误,则意味着您的用户列中没有包含’root’用户列值的行,并且该mysqld无法解析客户端的主机名:
Access denied for user ”@’unknown’
这些错误表明DNS问题。 要修复它,请执行mysqladmin flush-hosts以重置内部DNS主机缓存。
一些永久解决方案是:
- 确定你的DNS服务器出了什么问题并修复它。
- 在MySQL授权表中指定IP地址而不是主机名。
- 在Unix上的/etc/hosts或Windows上的\windows\hosts中放入客户机名称的条目。
- 使用–skip-name-resolve选项启动mysqld。
- 使用–skip-host-cache选项启动mysqld。
- 在Unix上,如果您在同一台计算机上运行服务器和客户端,请连接到本地主机。 对于到本地主机的连接,MySQL程序尝试使用Unix套接字文件连接到本地服务器,除非指定了连接参数以确保客户端进行TCP / IP连接。
- 在Windows上,如果您在同一台计算机上运行服务器和客户端,并且服务器支持命名管道连接,连接到主机名.(period),连接到. 使用名称管道而不是TCP/IP
- 如果mysql -u root工作正常,但是mysql -h your_hostname -u root导致Access被拒绝(其中your_hostname是本地主机的实际主机名),则用户表中的主机名称可能不正确。 这里的一个常见问题是用户表行中的主机值指定了非限定主机名,但系统的名称解析例程返回完全限定的域名(反之亦然)。 例如,如果你在用户表中有一行主机’pluto’,但你的DNS告诉MySQL你的主机名是’pluto.example.com’,那么这行不起作用。 尝试向包含主机IP地址的用户表添加一行作为主机列值。 (或者,您可以使用包含通配符的主机值向用户表中添加一行;例如,’pluto.%’。但是,使用以%结尾的主机值不安全,不建议使用!
- 如果mysql -u user_name有效,但mysql -u user_name some_db无效,则您尚未对名为some_db的数据库的给定用户,授予访问权限。
- 如果您无法弄清Access Access被拒绝的原因,请从用户表中删除具有包含通配符的主机值的所有行(包含’%’或’_’字符的行)。 一个非常常见的错误是插入一个新的行,其中Host =’%’和User =’some_user’,认为这使您可以指定本地主机从同一台机器进行连接。 这不起作用的原因是默认权限包含一个Host =’localhost’和User =”的行。 由于该行具有比’%’更具体的主机值“本地主机”,所以当从本地主机连接时,它优先用于新行! 正确的过程是插入Host =’localhost’和User =’some_user’的第二行,或删除Host =’localhost’和User =”的行。 删除该行后,请记住发出一个FLUSH PRIVILEGES语句来重新加载授权表。
- 如果您能够连接到MySQL服务器,但每当发出SELECT … INTO OUTFILE或LOAD DATA INFILE语句时都会收到Access denied消息,则用户表中的行不具有FILE权限。
- 如果您直接更改授权表(例如,通过使用INSERT,UPDATE或DELETE语句)并且您的更改似乎被忽略,请记住您必须执行FLUSH PRIVILEGES语句或mysqladmin flush-privileges命令以使服务器 重新加载特权表。 否则,只有在下次重新启动服务器时,更改才会生效。 请记住,使用UPDATE语句更改root密码后,在刷新特权之前,您不需要指定新密码,因为服务器不知道您已更改密码了!
- 如果您的权限在会话过程中似乎发生了变化,那么MySQL管理员可能会更改它们。 重新加载授权表会影响新的客户端连接,但它也会影响现有连接,如第6.2.6节“特权更改生效时”所述。
- 如果您在Perl,PHP,Python或ODBC程序中遇到访问问题,请尝试使用mysql -u user_name db_name或mysql -u user_name -pyour_pass db_name连接到服务器。 如果您能够使用mysql客户端进行连接,则问题在于您的程序,而不是访问权限。 (-p和密码之间没有空格;你也可以使用–password=your_pass语法来指定密码,如果你使用没有密码值的-p或–password选项,MySQL会提示你输入密码。)
- 出于测试目的,使用–skip-grant-tables选项启动mysqld服务器。 然后,您可以更改MySQL授权表并使用SHOW GRANTS语句来检查您的修改是否具有所需的效果。 当您对更改感到满意时,请执行mysqladmin flush-privileges来通知mysqld服务器重新加载权限。 这使您可以开始使用新授权表内容而无需停止并重新启动服务器。
- 如果一切都失败,请使用调试选项启动mysqld服务器(例如,–debug=d,general,query)。 这将打印有关尝试连接的主机和用户信息,以及有关每个发出的命令的信息。
- 如果您在MySQL授权表中存在任何其他问题,并且觉得必须将问题发布到邮件列表中,请始终提供MySQL授权表的转储。 您可以使用mysqldump mysql命令转储表。 要提交错误报告,请参阅第1.7节“如何报告错误或问题”中的说明。 在某些情况下,您可能需要使用–skip-grant-tables重新启动mysqld才能运行mysqldump。