MySQL 8.0——权限与安全管理

权限与安全管理

MySQL是一个多用户数据库,具有功能强大的访问控制系统,可以为不同用户指定允许的权限。

MySQL用户可以分为普通用户和root用户。

  • root用户是超级管理员,拥有所有权限,包括创建用户、删除用户和修改用户的密码等管理权限;
  • 普通用户只拥有被授予的各种权限。用户管理包括管理用户账户、权限等。

1、权限表

MySQL服务器通过权限表来控制用户对数据库的访问,权限表存放在MySQL数据库中,由MySQL_install_db脚本初始化。

存储账户权限信息的表主要有user、db、host、tables_priv、columns_priv和procs_priv。

1.1、user表

user表是MySQL中最重要的一个权限表,记录允许连接到服务器的账号信息,里面的权限是全局级的。

例如,一个用户在user表中被授予了DELETE权限,则该用户可以删除MySQL服务器上所有数据库中的任何记录。

MySQL 8.0中user表有42个字段,如表所示,这些字段可以分为4类,分别是用户列、权限列、安全列和资源控制列。

1.1.1、用户列

user表的用户列包括:

  • Host主机名
  • User用户名
  • authentication_string密码

其中User和Host为User表的联合主键。当用户与服务器之间建立连接时,输入的账户信息中的用户名称、主机名和密码必须匹配User表中对应的字段,只有3个值都匹配的时候,才允许连接的建立。

这3个字段的值就是创建账户时保存的账户信息。修改用户密码时,实际就是修改user表的authentication_string字段的值。

1.1.2、权限列

权限列的字段决定了用户的权限,描述了在全局范围内允许对数据和数据库进行的操作。

  • 包括查询权限、修改权限等普通权限
  • 还包括了关闭服务器、超级权限和加载用户等高级权限。

普通权限用于操作数据库;高级权限用于数据库管理。

user表中对应的权限是针对所有用户数据库的。这些字段值的类型为ENUM,可以取的值只能为YN,Y表示该用户有对应的权限;N表示用户没有对应的权限。

查看user表的结构可以看到,这些字段的值默认都是N。如果要修改权限,可以使用GRANT语句或UPDATE语句更改user表的这些字段来修改用户对应的权限。

1.1.3、安全列

安全列只有6个字段:

  • 两个是ssl相关的
  • 两个是x509相关的
  • 另外两个是授权插件相关的。

ssl用于加密;

x509标准可用于标识用户;

Plugin字段标识可以用于验证用户身份的插件,如果该字段为空,服务器使用内建授权验证机制验证用户身份。可以通过SHOW VARIABLES LIKE 'have_openssl'语句来查询服务器是否支持ssl功能。

1.1.4、资源控制列

资源控制列的字段用来限制用户使用的资源,包含4个字段,分别为:

  • max_questions------用户每小时允许执行的查询操作次数。
  • max_updates------用户每小时允许执行的更新操作次数。
  • max_connections------用户每小时允许执行的连接操作次数。
  • max_user_connections------用户允许同时建立的连接次数。

一个小时内用户查询或者连接数量超过资源控制限制,用户将被锁定,直到下一个小时,才可以在此执行对应的操作。可以使用GRANT语句更新这些字段的值。

1.2、db表

db表是MySQL数据中非常重要的权限表。db表中存储了用户对某个数据库的操作权限,决定用户能从哪个主机存取哪个数据库。db表比较常用。

db表的结构分别如表所示。

1.2.1、用户列

db表用户列有3个字段,分别是HostUserDb,标识从某个主机连接某个用户对某个数据库的操作权限,这3个字段的组合构成了db表的主键。

host表不存储用户名称,用户列只有2个字段,分别是Host和Db,表示从某个主机连接的用户对某个数据库的操作权限,其主键包括Host和Db两个字段。host很少用到,一般情况下db表就可以满足权限控制需求了。

1.2.2、权限列

db表中create_routine_privalter_routine_priv这两个字段表明用户是否有创建和修改存储过程的权限。

user表中的权限是针对所有数据库的,如果希望用户只对某个数据库有操作权限,那么需要将user表中对应的权限设置为N,然后在db表中设置对应数据库的操作权限。

例如,有一个名称为Zhangting的用户分别从名称为large.domain.com和small.domain.com的两个主机连接到数据库,并需要操作books数据库。这时,可以将用户名称Zhangting添加到db表中,而db表中的host字段值为空,然后将两个主机地址分别作为两条记录的host字段值添加到host表中,并将两个表的数据库字段设置为相同的值books。当有用户连接到MySQL服务器时,db表中没有用户登录的主机名称,则MySQL会从host表中查找相匹配的值,并根据查询的结果决定用户的操作是否被允许。

1.3、tables_priv表和columns_priv表

  • tables_priv表用来对表设置操作权限
  • columns_priv表用来对表的某一列设置权限

tables_priv表结构:

tables_priv表有8个字段,分别是Host、Db、User、Table_name、Grantor、Timestamp、Table_priv和Column_priv,各个字段说明如下:

  • HostDbUserTable_name 4个字段分表示主机名、数据库名、用户名和表名。
  • Grantor表示修改该记录的用户。
  • Timestamp字段表示修改该记录的时间。
  • Table_priv表示对表的操作权限,包括Select、Insert、Update、Delete、Create、Drop、Grant、References、Index和Alter。
  • Column_priv字段表示对表中的列的操作权限,包括Select、Insert、Update和References。

columns_priv表结构:

columns_priv表只有7个字段,分别是Host、Db、User、Table_name、Column_name、Timestamp、Column_priv。其中,Column_name用来指定对哪些数据列具有操作权限。

1.4、procs_priv表

procs_priv表可以对存储过程和存储函数设置操作权限。procs_priv的表结构如表所示。

procs_priv表包含8个字段,分别是Host、Db、User、Routine_name、Routine_type、Grantor、Proc_priv和Timestamp,各个字段的说明如下:

  • HostDbUser字段分别表示主机名、数据库名和用户名。Routine_name表示存储过程或函数的名称。
  • Routine_type表示存储过程或函数的类型。Routine_type字段有两个值,分别是FUNCTIONPROCEDURE:FUNCTION表示这是一个函数,PROCEDURE表示这是一个存储过程。
  • Grantor是插入或修改该记录的用户。
  • Proc_priv表示拥有的权限,包括ExecuteAlter RoutineGrant 3种。
  • Timestamp表示记录更新时间。

2、账户管理

MySQL提供了许多语句来管理用户账号,包括登录和退出MySQL服务器、创建用户、删除用户、密码管理和权限管理等内容。MySQL数据库的安全性需要通过账户管理来保证。

2.1、登录和退出MySQL服务器

通过MySQL --help命令可以查看MySQL命令帮助信息。MySQL命令的常用参数如下:

  • -h主机名,可以使用该参数指定主机名或ip,如果不指定,默认是localhost。
  • -u用户名,可以使用该参数指定用户名。
  • -p密码,可以使用该参数指定登录密码。如果该参数后面有一段字段,则该段字符串将作为用户的密码直接登录。如果后面没有内容,则登录的时候会提示输入密码。注意:该参数后面的字符串和-p之前不能有空格。
  • -P端口号,该参数后面接MySQL服务器的端口号,默认为3306。
  • 数据库名,可以在命令的最后指定数据库名。
  • -e执行SQL语句。如果指定了该参数,将在登录后执行-e后面的命令或SQL语句并退出。

使用root用户登录到本地MySQL服务器的mysql库中,命令如下:

sql 复制代码
mysql -h localhost -u root -p mysql

使用root用户登录到本地MySQL服务器的test_db数据库中,同时执行一条查询语句。命令如下:

sql 复制代码
mysql -h localhost -u root -p test_db -e "DESC person;"

2.2、新建普通用户

创建新用户,必须有相应的权限来执行创建操作。在MySQL数据库中,有两种方式创建新用户:

  • 一种是使用CREATE USER语句;
  • 另一种是直接操作MySQL授权表。

下面分别介绍这两种创建到用户的方法。

2.2.1、使用CREATE USER语句创建新用户

执行CREATE USER或GRANT语句时,服务器会修改相应的用户授权表,添加或者修改用户及其权限。CREATE USER语句的基本语法格式如下:

sql 复制代码
CREATE USER user_specification
	[, user_specification] ...

user_specification:
	user@host
	[
		IDENTIFIED BY [PASSWORD] 'password'
		| IDENTIFIED WITH auth_plugin [AS 'auth_string']
	]
  • user表示创建的用户的名称;
  • host表示允许登录的用户主机名称;
  • IDENTIFIED BY表示用来设置用户的密码;
  • PASSWORD表示使用哈希值设置密码,该参数可选;
  • 'password'表示用户登录时使用的普通明文密码;
  • IDENTIFIED WITH语句为用户指定一个身份验证插件;
  • auth_plugin是插件的名称,插件的名称可以是一个带单引号的字符串或者带双引号的字符串;
  • auth_string是可选的字符串参数,该参数将传递给身份验证插件,由该插件解释该参数的意义。

CREATE USER语句会添加一个新的MySQL账户。使用CREATE USER语句用户,必须有全局的CREATE USER权限或MySQL数据库的INSERT权限。
每添加一个用户,CREATE USER语句会在MySQL.user表中添加一条新记录,但是新创建的账户没有任何权限。如果添加的账户已经存在,CREATEUSER语句会返回一个错误。

使用CREATE USER创建一个用户,用户名是jeffrey,密码是mypass,主机名是localhost,命令如下:

sql 复制代码
create user 'jeffrey'@'localhost' identified by 'mypass';

如果只指定用户名部分'jeffrey',主机名部分则默认为'%'(对所有的主机开放权限)​。

user_specification告诉MySQL服务器当用户登录时怎么验证用户的登录授权。如果指定用户登录不需要密码,可以省略IDENTIFIEDBY部分:

sql 复制代码
create user 'jeffrey'@'localhost';

此种情况,MySQL服务端使用内建的身份验证机制,用户登录时不能指定密码。

如果要创建指定密码的用户,需要IDENTIFIED BY指定明文密码值:

sql 复制代码
create user 'jeffrey'@'localhost' identified by 'mypass';

此种情况,MySQL服务端使用内建的身份验证机制,用户登录时必须指定密码。

MySQL的某些版本中会引入授权表的结构变化,添加新的特权或功能。每当更新MySQL到一个新的版本时,应该更新授权表,以确保它们有最新的结构,确认可以使用任何新功能。

2.2.2、直接操作MySQL用户表

使用CREATE USER创建新用户时,实际上都是在user表中添加一条新的记录。因此,可以使用INSERT语句向user表中直接插入一条记录来创建一个新的用户。

使用INSERT语句,必须拥有对MySQL.user表的INSERT权限。使用INSERT语句创建新用户的基本语法格式如下:

sql 复制代码
INSERT INTO MySQL.user(Host, User, authentication_string)
VALUES('host', 'username', MD5('password'));

Host、User、authentication_string分别为user表中的主机、用户名称和密码字段;MD5()函数为密码加密函数。

使用INSERT创建一个新账户,其用户名称为customer1,主机名称为localhost,密码为aa123456,INSERT语句如下:

sql 复制代码
insert into mysql.user(Host, User, authentication_string) values
('localhost', 'customer1', md5('aa123456'));

语句执行失败,查看警告信息如下:

因为ssl_cipher、x509_issuer和x509_subject 3个字段在user表定义中没有设置默认值,所以在这里提示错误信息,影响INSERT语句的执行。使用SELECT语句查看user表中的记录:

可以看到新用户customer1并没有添加到user表中,表示添加新用户失败。

2.3、删除普通用户

在MySQL数据库中,可以使用DROP USER语句删除用户,也可以直接通过DELETE从MySQL.user表中删除对应的记录来删除用户。

2.3.1、使用DROP USER语句删除用户

DROP USER语句的语法如下:

sql 复制代码
DROP USER user [, user];

DROP USER语句用于删除一个或多个MySQL账户。要使用DROP USER,必须拥有MySQL数据库的全局CREATE USER权限或DELETE权限。

使用与GRANT或REVOKE相同的格式为每个账户命名。例如,​"'jeffrey'@'localhost'"账户名称的用户和主机部分与用户表记录的User和Host列值相对应。

使用DROP USER,可以删除一个账户和其权限,操作如下:

sql 复制代码
drop user 'user'@'localhost';

drop user;

第1条语句可以删除user在本地登录权限;第2条语句可以删除来自所有授权表的账户权限记录。

使用DROP USER删除账户"'jeffrey'@'localhost'"​,DROP USER语句如下:

sql 复制代码
drop user 'jeffrey'@'localhost';

可以看到语句执行成功,查看执行结果:

sql 复制代码
select host, user, authentication_string from mysql.user;

+-----------+------------------+------------------------------------------------------------------------+
| host      | user             | authentication_string                                                  |
+-----------+------------------+------------------------------------------------------------------------+
| %         | root             | *0E1740FE308BBD20F19DCD43896DB901B35E4EBB                              |
| localhost | mysql.infoschema | $A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED |
| localhost | mysql.session    | $A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED |
| localhost | mysql.sys        | $A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED |
+-----------+------------------+------------------------------------------------------------------------+

user表中已经没有名称为jeffrey、主机名为localhost的账户,即"jeffrey'@'localhost"的用户账号已经被删除。

DROP USER不能自动关闭任何打开的用户对话。而且,如果用户有打开的对话,此时取消用户,命令则不会生效,直到用户对话被关闭后才能生效。一旦对话被关闭,用户也被取消,此用户再次试图登录时将会失败。

2.3.2、使用DELETE语句删除用户

DELETE语句基本语法格式如下:

sql 复制代码
DELETE FROM MySQL.user 
WHERE host='hostname' and user='username'

host和user为user表中的两个字段,两个字段的组合确定所要删除的账户记录。

使用DELETE删除用户'customer1'@'localhost'。

首先创建用户customer1,命令如下:

sql 复制代码
create user 'customer1'@'localhost' identified by 'my123';

然后使用DELETE删除用户'customer1'@'localhost',命令如下:

sql 复制代码
DELETE FROM MySQL.user 
WHERE host= 'localhost' and user='customer1';

2.4、root用户修改自己的密码

root用户的安全对于保证MySQL的安全非常重要,因为root用户拥有很高的权限。下面讲述如何修改root用户的密码。

因为所有账户信息都保存在user表中,所以可以通过直接修改user表来改变root用户的密码。root用户登录到MySQL服务器后,使用UPDATE语句修改MySQL数据库的user表的authentication_string字段,从而修改用户的密码。使用UPDATA语句修改root用户密码的语句如下:

sql 复制代码
UPDATE mysql.user set authentication_string= MD5 ("123456") 
WHERE User="root" and Host="localhost";

PASSWORD()函数用来加密用户密码。执行UPDATE语句后,需要执行FLUSHPRIVILEGES语句重新加载用户权限。

使用UPDATE语句将root用户的密码修改为"123456"​。

使用root用户登录到MySQL服务器后,执行如下语句:

sql 复制代码
update mysql.user set authentication_string = md5("123456")
where User = "root" and Host = "localhost";

flush privileges;

执行完UPDATE语句后,root的密码被修改成了123456。使用FLUSH PRIVILEGES语句重新加载权限,就可以使用新的密码登录root用户了。

2.5、root用户修改普通用户密码

root用户拥有很高的权限,不仅可以修改自己的密码,还可以修改其他用户的密码。root用户登录MySQL服务器后,可以通过SET语句修改MySQL.user表、通过UPDATE语句修改用户的密码。

创建用户user,命令如下:

sql 复制代码
create user 'user'@'localhost' identified by 'my123';
2.5.1、使用SET语句修改普通用户的密码

使用SET语句修改普通用户密码的语法格式如下:

sql 复制代码
set password for 'user'@'localhost' = 'sa123';

使用SET语句将user用户的密码修改为"sa123"​。

使用root用户登录到MySQL服务器后,执行如下语句:

sql 复制代码
set password for 'user'@'localhost' = 'sa123';

SET语句执行成功,User用户的密码被成功设置为sa123。

2.5.2、使用UPDATE语句修改普通用户的密码

使用root用户登录到MySQL服务器后,可以使用UPDATE语句修改MySQL数据库的user表的password字段,从而修改普通用户的密码。使用UPDATA语句修改用户密码的语法如下:

sql 复制代码
UPDATE MySQL.user SET authentication_string=MD5("123456")
WHERE User="username" AND Host="hostname";

MD5()函数用来加密用户密码。执行UPDATE语句后,需要执行FLUSH PRIVILEGES语句重新加载用户权限。

使用UPDATE语句将user用户的密码修改为"sns123"​。

使用root用户登录到MySQL服务器后,执行如下语句:

sql 复制代码
update mysql.user set authentication_string = md5("sna123")
where User = "user" and Host = "localhost";

flush privileges;

执行完UPDATE语句后,user的密码被修改成了sns123。使用FLUSH PRIVILEGES重新加载权限,就可以使用新的密码登录user用户了。

3、权限管理

权限管理主要是对登录到MySQL的用户进行权限验证。所有用户的权限都存储在MySQL的权限表中,不合理的权限规划会给MySQL服务器带来安全隐患。

数据库管理员要对所有用户的权限进行合理规划管理。MySQL权限系统的主要功能是证实连接到一台给定主机的用户,并且赋予该用户在数据库上的SELECT、INSERT、UPDATE和DELETE权限。

3.1、MySQL的各种权限

账户权限信息被存储在MySQL数据库的user、db、host、tables_priv、columns_priv和procs_priv表中。在MySQL启动时,服务器将这些数据库表中权限信息的内容读入内存。

GRANT和REVOKE语句所涉及的权限的名称如表所示,其中还有在授权表中每个权限的表列名称和每个权限有关的操作对象等。

  • CREATEDROP权限,可以创建新数据库和表,或删除(移掉)已有数据库和表。如果将MySQL数据库中的DROP权限授予某用户,用户可以删掉MySQL访问权限保存的数据库。
  • SELECTINSERTUPDATEDELETE权限允许在一个数据库现有的表上实施操作。
  • SELECT权限只有在它们真正从一个表中检索行时才被用到。
  • INDEX权限允许创建或删除索引,INDEX适用已有表。如果具有某个表的CREATE权限,可以在CREATE TABLE语句中包括索引定义。
  • ALTER权限,可以使用ALTER TABLE来更改表的结构和重新命名表。
  • CREATE ROUTINE权限用来创建保存的程序(函数和程序),ALTER ROUTINE权限用来更改和删除保存的程序,EXECUTE权限用来执行保存的程序。
  • GRANT权限允许授权给其他用户,可用于数据库、表和保存的程序。
  • FILE权限给予用户使用LOAD DATAINFILE和SELECT ... INTO OUTFILE语句读或写服务器上的文件,任何被授予FILE权限的用户都能读或写MySQL服务器上的任何文件(说明用户可以读任何数据库目录下的文件,因为服务器可以访问这些文件)。FILE权限允许用户在MySQL服务器具有写权限的 目录下创建新文件,但不能覆盖已有文件。
  • 其余的权限用于管理性操作,它使用MySQLadmin程序或SQL语句实施。

下表显示每个权限允许执行的MySQLadmin命令。

  • reload命令告诉服务器将授权表重新读入内存;
    • flush-privileges是reload的同义词;
    • refresh命令清空所有表并关闭/打开记录文件;
    • 其他flush-xxx命令执行类似refresh的功能,但是范围更有限,并且在某些情况下可能更好用。例如,如果只是想清空记录文件,flush-logs是比refresh更好的选择。
  • shutdown命令关掉服务器。只能从MySQLadmin发出命令。
  • processlist命令显示在服务器内执行的线程的信息(其他账户相关的客户端执行的语句)。kill命令杀死服务器线程。用户总是能显示或杀死自己的线程,但是需要PROCESS权限来显示或杀死其他用户和SUPER权限启动的线程。
  • kill命令能用来终止其他用户或更改服务器的操作方式。

3.2、授权

授权就是为某个用户授予权限。合理的授权可以保证数据库的安全。MySQL中可以使用GRANT语句为用户授予权限。

授予的权限可以分为多个层级:

1. 全局层级

全局权限适用于一个给定服务器中的所有数据库。这些权限存储在MySQL.user表中。

GRANT ALL ON *.*REVOKE ALL ON *.*只授予和撤销全局权限。

2. 数据库层级

数据库权限适用于一个给定数据库中的所有目标。这些权限存储在MySQL.dbMySQL.host表中。

GRANT ALL ON db_name.*REVOKE ALL ON db_name.*只授予和撤销数据库权限。

3. 表层级

表权限适用于一个给定表中的所有列。这些权限存储在MySQL.talbes_priv表中。

GRANT ALL ON db_name.tbl_nameREVOKE ALL ON db_name.tbl_name只授予和撤销表权限。

4. 列层级

列权限适用于一个给定表中的单一列。这些权限存储在MySQL.columns_priv表中。当使用REVOKE时,必须指定与被授权列相同的列。

5. 子程序层级

CREATE ROUTINEALTER ROUTINEEXECUTEGRANT权限适用于已存储的子程序。这些权限可以被授予为全局层级和数据库层级。

而且,除了CREATE ROUTINE外,这些权限可以被授予子程序层级,并存储在MySQL.procs_priv表中。

在MySQL中,必须是拥有GRANT权限的用户才可以执行GRANT语句。

要使用GRANT或REVOKE,必须拥有GRANT OPTION权限,并且必须用于正在授予或撤销的权限。GRANT的语法如下:

sql 复制代码
GRANT priv_type [(columns)] [, priv_type [(columns)]] ...
ON [object_type] talbe1, table2, ..., tablen
TO user [WITH GRANT OPTION]

object_type = TABLE | FUNCTION | PROCEDURE

//即
GRANT 权限列表
ON 数据库.表
TO '用户名'@'主机'
[IDENTIFIED BY '密码']
[WITH GRANT OPTION];
  • priv_type参数表示权限类型;
  • columns参数表示权限作用于哪些列上,不指定该参数,表示作用于整个表;
  • table1,table2,...,tablen表示授予权限的列所在的表;
  • object_type指定授权作用的对象类型包括TABLE(表)、FUNCTION(函数)和PROCEDURE(存储过程),当从旧版本的MySQL升级时,要使用object_tpye子句,必须升级授权表;
  • user参数表示用户账户,由用户名和主机名构成,形式是"'username'@'hostname'";
  • IDENTIFIED BY参数用于设置密码。
  • WITH关键字后可以跟一个或多个with_option参数。这个参数有5个选项,意义如下:
    • GRANT OPTION:被授权的用户可以将这些权限赋予别的用户。
    • MAX_QUERIES_PER_HOUR count:设置每个小时可以执行count次查询。
    • MAX_UPDATES_PER_HOUR count:设置每小时可以执行count次更新。
    • MAX_CONNECTIONS_PER_HOUR count:设置每小时可以建立count个连接。
    • MAX_USER_CONNECTIONS count:设置单个用户可以同时建立count个连接。

使用GRANT语句创建一个新的用户grantUser,密码为"grantpwd"​。用户grantUser对所有的数据有查询、插入权限,并授于GRANT权限。GRANT语句及其执行结果如下:

sql 复制代码
//8.0+中grant无法直接创建用户并授权,需要分部进行
create user 'grantUser'@'localhost' identified by 'grantpwd';

grant select, insert on *.* 
to 'grantUser'@'localhost'
with grant option;

//5.7中可以
grant select, insert on *.* 
to 'grantUser'@'localhost'
identified by 'grantpwd'
with grant option;

使用SELECT语句查询用户grantUser的权限:

sql 复制代码
select Host, User, Select_priv, Insert_priv, Grant_priv
from mysql.user 
where user = 'grantUser';

+-----------+-----------+-------------+-------------+------------+
| Host      | User      | Select_priv | Insert_priv | Grant_priv |
+-----------+-----------+-------------+-------------+------------+
| localhost | grantUser | Y           | Y           | Y          |
+-----------+-----------+-------------+-------------+------------+

查询结果显示用户grantUser被创建成功,并被赋予SELECT、INSERT和GRANT权限,其相应字段值均为'Y'。

被授予GRANT权限的用户可以登录MySQL并创建其他用户账户,在这里为名称是grantUser的用户。

3.3、收回权限

收回权限就是取消已经赋予用户的某些权限。收回用户不必要的权限可以在一定程度上保证系统的安全性。

MySQL中使用REVOKE语句取消用户的某些权限。使用REVOKE收回权限之后,用户账户的记录将从db、host、tables_priv和columns_priv表中删除,但是用户账号记录仍然在user表中保存(删除user表中的账户记录,使用DROPUSER语句)​。

在将用户账户从user表删除之前,应该收回相应用户的所有权限。REVOKE语句有两种语法格式。

第一种语法是收回所有用户的所有权限,用于取消对于已命名的用户的所有全局层级、 数据库层级、表层级和列层级的权限,具体如下:

sql 复制代码
REVOKE ALL PRIVILEGES, GRANT OPTION
FROM 'user'@'host' [, 'user'@'host' ...]

REVOKE语句必须和FROM语句一起使用。FROM语句指明需要收回权限的账户。

另一种为长格式的REVOKE语句,基本语法如下

sql 复制代码
REVOKE priv_type [(columns)] [, priv_type [(columns)]] ...
ON table1, table2,..., tablen
FROM 'user'@'host'[, 'user'@ 'host' ...]
  • priv_type参数表示权限类型;
  • columns参数表示权限作用于哪些列上,如果不指定该参数,表示作用于整个表;
  • table1,table2,...,tablen表示从哪个表中收回权限;
  • 'user'@'host'参数表示用户账户,由用户名和主机名构成。

要使用REVOKE语句,必须拥有MySQL数据库的全局CREATE USER权限或UPDATE权限。

使用REVOKE语句取消用户user的更新权限。REVOKE语句及其执行结果如下:

sql 复制代码
revoke update on *.*
from 'user'@'localhost';

当从旧版本的MySQL升级时,如果要使用EXECUTE、CREATE VIEW、SHOW VIEW、CREATE USER、CREATE ROUTINE和ALTER ROUTINE权限,必须首先升级授权表。

3.4、查看权限

SHOW GRANTS语句可以显示指定用户的权限信息,使用SHOW GRANT查看账户信息的基本语法格式如下:

sql 复制代码
SHOW GRANTS FOR 'user'@'host' ;
  • user表示登录用户的名称
  • host表示登录的主机名称或者IP地址。

在使用该语句时,要确保指定的用户名和主机名都要用单引号括起来,并使用'@'符号将两个名字分隔开。

使用SHOW GRANTS语句查询用户user的权限信息。SHOW GRANTS语句及其执行结果如下:

sql 复制代码
show grants for 'user'@'localhost';

+------------------------------------------+
| Grants for user@localhost                |
+------------------------------------------+
| GRANT USAGE ON *.* TO `user`@`localhost` |
+------------------------------------------+

在这里,只是定义了个别的用户权限,GRANT可以显示更加详细的权限信息,包括全局级的和非全局级的权限,如果表层级或者列层级的权限被授予用户,那么它们也能在结果中显示出来。

在前面创建用户时,查看新建的账户时使用SELECT语句,也可以通过SELECT语句查看user表中的各个权限字段以确定用户的权限信息,其基本语法格式如下:

sql 复制代码
SELECT privileges_list 
FROM user 
WHERE user='username', host= 'hostname';

其中,privileges_list为想要查看的权限字段,可以为Select_priv、Insert_priv等。

4、访问控制

正常情况下,并不希望每个用户都可以执行所有的数据库操作。当MySQL允许一个用户执行各种操作时,它将首先核实该用户向MySQL服务器发送的连接请求,然后确认用户的操作请求是否被允许。本节将向读者介绍MySQL中的访问控制过程。MySQL的访问控制分为两个阶段:

  • 连接核实阶段
  • 请求核实阶段

4.1、连接核实阶段

当连接MySQL服务器时,服务器基于用户的身份以及用户是否能通过正确的密码身份验证来接受或拒绝连接,即客户端用户连接请求中会提供用户名称、主机地址名和密码。

MySQL使用user表中的3个字段(Host、User和authentication_string)执行身份检查,服务器只有在user表记录的Host和User字段匹配客户端主机名和用户名并且提供正确的密码时才接受连接。

如果连接核实没有通过,服务器完全拒绝访问;否则,服务器接受连接,然后进入阶段2等待用户请求。

4.2、请求核实阶段

建立了连接之后,服务器进入访问控制的阶段2。对在此连接上的每个请求,服务器检查用户要执行的操作,然后检查是否有足够的权限来执行它。这正是在授权表中的权限列发挥作用的地方。这些权限可以来自user、db、host、tables_priv或columns_priv表。

  1. 确认权限时,MySQL首先检查user表,如果指定的权限没有在user表中被授权;
  2. MySQL将检查db表,db表是下一安全层级,其中的权限限定于数据库层级,在该层级的SELECT权限允许用户查看指定数据库所有表中的数据;
  3. 如果在该层级没有找到限定的权限,则MySQL继续检查tables_priv表以及columns_priv表,如果所有权限表都检查完毕,但还是没有找到允许的权限操作,MySQL将返回错误信息,用户请求的操作不能执行,操作失败。

MySQL通过向下层级的顺序检查权限表(从user表到columns_priv表)​,但并不是所有的权限都要执行该过程。例如,一个用户登录到MySQL服务器之后只执行对MySQL的管理操作,此时,只涉及管理权限,因此MySQL只检查user表。另外,如果请求的权限操作不被允许,MySQL也不会继续检查下一层级的表。

5、提升安全性

5.1、AES 256加密

MySQL 8.0支持多种AES 256加密模式,通过更大的密钥长度和不同的块模式增强了高级加密标准。

这里主要通过加密函数AES_ENCODE()和解密函数AES_DECODE()来提高安全强度。

5.1.1、AES_ENCODE()

该函数的语法格式如下:

sql 复制代码
AES_ENCODE(str,pswd_str)

其中,str为需要加密的字符串,参数pswd_str是密钥。

下面通将字符串' Adversity does teach whoyour real friends are'加密,密钥为'key10001',加密后的串存在@ss中。输入 语句如下:

sql 复制代码
set @ss = aes_encrypt('Adversity does teach who your real friends are', 'key10001');

查看加密后的字符串,输入语句如下:

sql 复制代码
select @ss;

+----------------------------------------------------------------------------------------------------+
| @ss                                                                                                |
+----------------------------------------------------------------------------------------------------+
| 0x4847c3b5326f650497d2a6947b6f366e7ca9034b4c1bb630d00048965341a4362d15fb383e678b222e176c73ec9681c5 |
+----------------------------------------------------------------------------------------------------+

查看加密后字符串的长度。输入语句如下:

sql 复制代码
select char_length(@ss);

+------------------+
| char_length(@ss) |
+------------------+
|               48 |
+------------------+
5.1.2、AES_DECODE()

该函数的语法格式如下:

sql 复制代码
AES_ENCODE(str,pswd_str)

其中,str为需要解密的字符串,参数pswd_str是密钥。

下面将@ss中的字符串解密。输入语句如下:

sql 复制代码
select aes_decrypt(@ss, 'key10001');

+------------------------------------------------------------------------------------------------+
| aes_decrypt(@ss, 'key10001')                                                                   |
+------------------------------------------------------------------------------------------------+
| 0x41647665727369747920646f65732074656163682077686f20796f7572207265616c20667269656e647320617265 |
+------------------------------------------------------------------------------------------------+
1.5.3、将加密字符串存入数据表中

用户将加密的字符串存入数据表的过程中经常会出问题,下面将通过案例来学习其方法。

创建数据表mm,包含3个字段,属性分别为varbinary、binary、blob。输入语句如下:

sql 复制代码
create table mm
(
	s1 varbinary(16),
	s2 binary(16),
	s3 blob
);

将'雨里一两家'、'two things'、'闲看栀子花'加密,密钥为key,存入数据表mm中。输入语句如下:

sql 复制代码
insert into mm values
(aes_encrypt('雨里一两家', 'key'), aes_encrypt('twothings', 'key'), aes_encrypt('闲看栀子花', 'key'));

解密数据表mm的内容,输入语句如下:

sql 复制代码
SELECT
  CAST(AES_DECRYPT(s1, 'key') AS CHAR CHARACTER SET utf8mb4) AS s1,
  CAST(AES_DECRYPT(s2, 'key') AS CHAR CHARACTER SET utf8mb4) AS s2,
  CAST(AES_DECRYPT(s3, 'key') AS CHAR CHARACTER SET utf8mb4) AS s3
FROM mm;

+------------+-----------+------------+
| s1         | s2        | s3         |
+------------+-----------+------------+
| 雨里一两家 | twothings | 闲看栀子花 |
+------------+-----------+------------+

5.2、密码到期更换策略

MySQL 8.0允许数据库管理员手动设置账户密码过期时间。任何密码超期的账户想要连接服务端时都必须更改密码。通过设置default_password_lifetime参数可以设置账户过期时间。

首先查看系统中的账户过期时间。输入语句如下:

sql 复制代码
select user, host, password_last_changed, password_lifetime, password_expired
from mysql.user \G;

+------------------+-----------+-----------------------+-------------------+------------------+
| user             | host      | password_last_changed | password_lifetime | password_expired |
+------------------+-----------+-----------------------+-------------------+------------------+
| root             | %         | 2026-06-12 20:12:14   |            <null> | N                |
| customer1        | localhost | 2026-06-26 15:27:28   |            <null> | N                |
| grantUser        | localhost | 2026-06-26 16:18:40   |            <null> | N                |
| mysql.infoschema | localhost | 2026-06-12 19:58:25   |            <null> | N                |
| mysql.session    | localhost | 2026-06-12 19:58:25   |            <null> | N                |
| mysql.sys        | localhost | 2026-06-12 19:58:25   |            <null> | N                |
| user             | localhost | 2026-06-26 15:37:52   |            <null> | N                |
+------------------+-----------+-----------------------+-------------------+------------------+

从结果可以看出,password_lifetime: NULL表示密码永不过期。

下面设置root用户的密码过期时间为260天,输入语句如下:

sql 复制代码
alter user root@localhost password expire interval 260 day;

将root用户的密码过期重新设置为永不过期,输入语句如下:

sql 复制代码
alter user root@localhost password expire default;

5.3、安全模式安装

MySQL新增了"安全模式"的安装形式,从而可以避免用户的数据被泄漏。用户可以通过以下方式来提升MySQL安装的安全性:

  1. 为root账户设置密码。
  2. 移除能从本地主机以外的地址访问数据库的root账户。
  3. 移除匿名账户。
  4. 移除test数据库,该数据库默认可被任意用户甚至匿名账户访问。

使用mysqld --initialize命令来安装MySQL实例默认是安全的,主要原因如下:

  • 在安装过程只创建一个root账户'root'@'localhost',自动为这个账户生成一个随机密码并标记密码过期。
  • 数据库管理员必须用root账户及该随机密码登录并设置一个新密码后才能对数据库进行正常操作。
  • 安装过程不创建任何匿名账户。
  • 安装过程不创建test数据库。

6、MySQL 8.0的新特性------管理角色

在MySQL 8.0数据库中,角色可以看成是一些权限的集合,为用户赋予统一的角色,权限的修改直接通过角色来进行,无须为每个用户单独授权。

创建角色,执行语句如下:

sql 复制代码
create role role_tt;

给角色授予权限,执行语句如下:

sql 复制代码
grant select on db.* 
to 'role_tt';

创建用户myuser1,执行语句如下:

sql 复制代码
create user 'myuser1'@'%' identified by '123456';

为用户myuser1赋予角色role_tt,执行语句如下:

sql 复制代码
grant 'role_tt' to 'myuser1'@'%';

给角色role_tt增加insert权限,执行语句如下:

sql 复制代码
grant insert on db.* to 'role_tt';

给角色role_tt删除insert权限,执行语句如下:

sql 复制代码
revoke insert on db.* 
from 'role_tt';

查看默认角色信息,执行语句如下:

sql 复制代码
select * from mysql.default_roles;

+------+------+-------------------+-------------------+
| HOST | USER | DEFAULT_ROLE_HOST | DEFAULT_ROLE_USER |
+------+------+-------------------+-------------------+
+------+------+-------------------+-------------------+

查看角色与用户关系,执行语句如下:

sql 复制代码
select * from mysql.role_edges;

+-----------+-----------+---------+---------+-------------------+
| FROM_HOST | FROM_USER | TO_HOST | TO_USER | WITH_ADMIN_OPTION |
+-----------+-----------+---------+---------+-------------------+
| %         | role_tt   | %       | myuser1 | N                 |
+-----------+-----------+---------+---------+-------------------+

删除角色,执行语句如下:

sql 复制代码
drop role role_tt;

7、综合案例

1. 打开MySQL客户端工具,输入登录命令,登录MySQL。

sql 复制代码
  C:\>mysql -u root -p
  Enter password: **

输入正确密码,按回车键,出现欢迎信息,表示登录成功。

2. 选择MySQL数据库为当前数据库。

sql 复制代码
  MySQL> use mysql;
  Database changed

3. 创建新账户,用户名称为newAdmin,密码为pw123。

使用GRANT语句创建新账户,创建过程如下:

sql 复制代码
create user 'newAdmin'@'localhost' identified by 'pw123';

4. 分别从user表中查看新账户的账户信息。

用户账户创建完成之后,账户信息已经保存在user表中,查询user名称为newAdmin的账户信息,执行过程如下:

sql 复制代码
select host, user, select_priv, update_priv
from user
where user = 'newAdmin';

+-----------+----------+-------------+-------------+
| host      | user     | select_priv | update_priv |
+-----------+----------+-------------+-------------+
| localhost | newAdmin | N           | N           |
+-----------+----------+-------------+-------------+

使用SHOW GRANTS语句查看newAdmin的权限信息。

查看newAdmin账户的权限信息,输入语句如下:

sql 复制代码
show grants for 'newAdmin'@'localhost';

+----------------------------------------------+
| Grants for newAdmin@localhost                |
+----------------------------------------------+
| GRANT USAGE ON *.* TO `newAdmin`@`localhost` |
+----------------------------------------------+

使用newAdmin用户登录MySQL。

退出当前登录,使用EXIT命令,语句如下:

sql 复制代码
exit

使用newAdmin账户登录MySQL,语句如下:

sql 复制代码
  C:\>MySQL -u newAdmin -p
  Enter password: ***

输入密码正确后,出现"MySQL>"提示符,登录成功。

7. 使用newAdmin用户查看test_db数据库中person表中的数据。

newAdmin用户被授予test_db数据库中person表中3个字段上的查询权限,因此可以执行SELECT语句查看这几个字段的值,执行过程如下:

sql 复制代码
select * from test_db.person limit 5;

8. 使用newAdmin用户向person表中插入一条新记录,查看语句执行结果。

sql 复制代码
insert into test_db.person(name, age, info) values
(gaga', 30);

语句不能执行,错误信息表明newAdmin用户不能对person表进行插入操作。因此,用户不可以执行没有被授权的操作语句。

9. 退出当前登录,使用root用户重新登录,收回newAdmin账户的权限。

输入退出命令:

sql 复制代码
exit

重新以root用户登录MySQL,并选择MySQL数据库为当前数据库。

输入语句收回newAdmin账户的权限,执行过程如下:

sql 复制代码
revoke select, update on test_db.person
from 'newAdmin'@'localhost';

10. 删除newAdmin的账户信息。

删除指定账户,可以使用DROP USER语句,输入如下:

sql 复制代码
drop user 'newAdmin'@'localhost';

语句执行成功之后,tables_priv和columns_priv中相关的记录将被删除。

8、常见问题

8.1、已经将一个账户的信息从数据库中完全删除,为什么该用户还能登录数据库?

出现这种情况的原因可能有多种,最有可能的是在user数据表中存在匿名账户。在user表中匿名账户的User字段值为空字符串,这会允许任何人连接到数据库。检测是否存在匿名登录用户的方法是输入以下语句:

sql 复制代码
select * from user 
where User = '';

如果有记录返回,则说明存在匿名用户,需要删除该记录,以保证数据库的访问安全。删除语句为:

sql 复制代码
delete from user
where user = '';

这样一来,该账户就肯定不能登录MySQL服务器了。

8.2、应该使用哪种方法创建用户?

创建用户的几种方法:GRANT语句、CREATE USER语句和直接操作user表。

一般情况下,最好使用GRANT或者CREATE USER语句,而不要直接将用户信息插入user表,因为user表中存储了全局级别的权限以及其他的账户信息,如果意外破坏了user表中的记录,则可能会对MySQL服务器造成很大影响。