MySQL不看视频 · SSH 实操

第五单元 · 账号与权限只读 · 第 12 / 21 节

账号与权限:谁能连、能做什么

先确认自己是谁(CURRENT_USER 的“用户名@主机”),再逐行读懂 SHOW GRANTS 的“权限 + 范围”,分清 USAGE 只是能登录;然后理解权限的四层范围与最小权限,认识 1227 / 1044 / 1142 各发生在哪一层。读者侧全部只读,建用户与授权只在作者隔离实例演示并注明。

2 个步骤 · 进度只存本机

约 18 分钟。这一节不建表、不改数据,只回答两个问题:我现在是哪个账号?这个账号被允许做什么?读者侧能真正执行的只有三条只读语句;建用户、授权这些管理动作,本页只讲语法,并在文字里说明共享练习库为什么会拒绝。

SSH / Linux 终端MySQL 终端输出示例(只读)复制按钮只复制命令,不含 $ / mysql> 提示符和输出。

已完成 0 / 2 步

你现在在哪个提示符

应该在 mysql>。断了就先回到学习终端(PowerShell):

SSH / Linux 终端bash
ssh root@8.148.204.152
SSH / Linux 终端bash
mysql-learning

进去以后确认当前库:

MySQL 终端sql
SELECT DATABASE();
输出示例(只读,不要粘贴执行)text不可复制
+------------+
| DATABASE() |
+------------+
| itcast     |
+------------+
1 row in set (0.00 sec)

显示 itcast 就对了。如果是 NULL,执行 USE itcast;——它只切换当前连接用哪个库,不动数据。

第一步:账号是“用户名@主机”

平时登录只输入一个用户名,但在 MySQL 内部,账号的完整身份是 用户名 + 来源主机。同一个用户名从不同机器连进来,会被当成两个不同的账号,权限也各算各的。

先看自己是谁:

MySQL 终端sql
SELECT CURRENT_USER(), USER();
输出示例(只读,不要粘贴执行)text不可复制
+-------------------------+-------------------------+
| CURRENT_USER()          | USER()                  |
+-------------------------+-------------------------+
| mx_p24_reader@localhost | mx_p24_reader@localhost |
+-------------------------+-------------------------+
1 row in set (0.00 sec)

两列看起来一样。CURRENT_USER() 是服务器实际认证的账号,权限按它算;USER() 是你这次连接报上来的身份。正常情况下两者相同,看到不同通常说明连接方式特殊,本节不展开。

@ 前面是用户名,后面是主机。这份输出是在作者隔离实例的受限账号上抓的(mx_p24_reader@localhost);你在共享练习库执行时,会看到 course@… 的形式,主机部分取决于你从哪里连进来——名字不同,读法一样。

再看这个账号被给了什么:

MySQL 终端sql
SHOW GRANTS;
输出示例(只读,不要粘贴执行)text不可复制
+--------------------------------------------------------------+
| Grants for mx_p24_reader@localhost                           |
+--------------------------------------------------------------+
| GRANT USAGE ON *.* TO `mx_p24_reader`@`localhost`            |
| GRANT SELECT ON `mx_p24_db`.* TO `mx_p24_reader`@`localhost` |
+--------------------------------------------------------------+
2 rows in set (0.00 sec)

输出是“一行一条授权”,从右往左读三件事:

  • TO 后面是账号:mx_p24_reader@localhost。
  • ON 后面是范围:第一行 *.* 是全局,第二行 mx_p24_db.* 是某一个库里的所有表。
  • GRANT 后面是权限列表:第一行只有 USAGE,第二行是 SELECT。

USAGE 很容易误会:它不代表有任何权限,只表示“这个账号存在、能登录”。所以这两行的真实含义是:能连进来,但只能在 mx_p24_db 这个库里查数据,别的什么都做不了。

你在共享库执行 SHOW GRANTS;,会看到 ON itcast.* 这样的行,权限列表可能比这里长(练习库要能建表、改数据)。读法完全相同:先看范围,再看权限。

说明 建用户、改密码、删用户的语法(共享练习库没有这个权限,别在这里执行)

下面三行只是语法说明,不要在共享练习库里粘贴执行:

输出示例(只读,不要粘贴执行)text不可复制
CREATE USER '用户名'@'主机' IDENTIFIED BY '占位口令';
ALTER USER '用户名'@'主机' IDENTIFIED BY '新占位口令';
DROP USER '用户名'@'主机';
  • 主机写 'localhost' 表示只允许从本机连,写 '%' 表示任意主机;范围越具体越安全。
  • IDENTIFIED BY 后面是口令。真实环境里不要把口令写进脚本、聊天记录或截图;本页一律用占位串 ChangeMe_01。
  • 建账号是全局管理动作,需要 CREATE USER 权限。共享练习库的 course 账号没有这个权限,本轮也不申请扩大它。
说明 为什么在共享库建用户会被拒:1227 的真实样子

在作者隔离实例里,用同一个受限账号尝试建用户:

输出示例(只读,不要粘贴执行)text不可复制
CREATE USER 'mx_p24_x'@'localhost' IDENTIFIED BY 'ChangeMe_01';
输出示例(只读,不要粘贴执行)text不可复制
ERROR 1227 (42000): Access denied; you need (at least one of) the CREATE USER privilege(s) for this operation

1227 的意思是“你没有这个全局权限”,和语法写得对不对无关。同一类错误已在隔离实例复现,未在共享库执行;要建账号,只能由服务器管理员在服务器上做,不属于本课程的练习范围。

同一个受限账号去切到不属于它的库,会被 1044 拦住:

MySQL 终端sql
USE itcast;
输出示例(只读,不要粘贴执行)text不可复制
ERROR 1044 (42000): Access denied for user 'mx_p24_reader'@'localhost' to database 'itcast'

1044 是“对这个库没有任何权限”。共享库的 course 账号不会被拒,因为它的授权范围就是 itcast——这也是为什么你在共享库能直接 USE itcast;。这两个输出都来自隔离实例的受限账号,不是共享库的截图;你在共享库执行 USE itcast; 会成功,正是权限差异的体现。

第二步:权限 = 操作 + 范围,按最小需要给

一条授权由两部分组成:能做什么(权限列表)和对什么做(范围)。范围从大到小有四层:

范围写法管到哪里典型用途
*.*整个实例的所有库管理员;普通账号不该有
库名.*一个库里的所有表只在一个业务库里干活
库名.表名一张表只允许碰某一张表
库名.表名(列名)一张表的某几列只让改某一列,很少用

管理授权用 GRANT,收回用 REVOKE,语法形状是:

输出示例(只读,不要粘贴执行)text不可复制
GRANT 权限列表 ON 范围 TO '用户名'@'主机';
REVOKE 权限列表 ON 范围 FROM '用户名'@'主机';

同样只是语法说明:共享练习库没有授权权限,别在这里执行。GRANT 能成功,前提是执行者自己持有这些权限、并且有 GRANT OPTION;course 账号两者都没有。

读者侧能做的,是把自己的授权读清楚:

MySQL 终端sql
SHOW GRANTS FOR CURRENT_USER();
输出示例(只读,不要粘贴执行)text不可复制
+--------------------------------------------------------------+
| Grants for mx_p24_reader@localhost                           |
+--------------------------------------------------------------+
| GRANT USAGE ON *.* TO `mx_p24_reader`@`localhost`            |
| GRANT SELECT ON `mx_p24_db`.* TO `mx_p24_reader`@`localhost` |
+--------------------------------------------------------------+
2 rows in set (0.00 sec)

它和 SHOW GRANTS; 的内容一样,好处是不用记自己的主机名——CURRENT_USER() 会自动填上。养成习惯:动手前先读一遍自己的授权,比猜能不能做可靠。

说明 隔离实例里的三步演练:只给 SELECT → 加一条 INSERT → 再收回

下面这些语句只在作者隔离实例里执行过(账号 mx_p25_app@localhost,库 mx_p25_db),共享练习库不能执行。放这里是为了让你看到“权限改了,行为立刻变”。

第 1 步:只给库级 SELECT。

输出示例(只读,不要粘贴执行)text不可复制
GRANT SELECT ON mx_p25_db.* TO 'mx_p25_app'@'localhost';

这个账号自己的视角:

MySQL 终端sql
SHOW GRANTS;
输出示例(只读,不要粘贴执行)text不可复制
+-----------------------------------------------------------+
| Grants for mx_p25_app@localhost                           |
+-----------------------------------------------------------+
| GRANT USAGE ON *.* TO `mx_p25_app`@`localhost`            |
| GRANT SELECT ON `mx_p25_db`.* TO `mx_p25_app`@`localhost` |
+-----------------------------------------------------------+
2 rows in set (0.00 sec)
MySQL 终端sql
SELECT id, v FROM mx_p25_db.t ORDER BY id;
输出示例(只读,不要粘贴执行)text不可复制
+----+------+
| id | v    |
+----+------+
|  1 | a    |
+----+------+
1 row in set (0.00 sec)
MySQL 终端sql
INSERT INTO mx_p25_db.t VALUES (2, 'b');
输出示例(只读,不要粘贴执行)text不可复制
ERROR 1142 (42000): INSERT command denied to user 'mx_p25_app'@'localhost' for table 't'

查询可以,写入被拒:1142 是“这个操作在这张表上被拒绝”,比 1044 更具体——库进得去,但 INSERT 这个动作没授权。这就是最小权限:只给工作需要的那一个。

第 2 步:再加一条表级 INSERT。

输出示例(只读,不要粘贴执行)text不可复制
GRANT INSERT ON mx_p25_db.t TO 'mx_p25_app'@'localhost';
MySQL 终端sql
INSERT INTO mx_p25_db.t VALUES (2, 'b');
输出示例(只读,不要粘贴执行)text不可复制
Query OK, 1 row affected (0.00 sec)
MySQL 终端sql
SHOW GRANTS;
输出示例(只读,不要粘贴执行)text不可复制
+-------------------------------------------------------------+
| Grants for mx_p25_app@localhost                             |
+-------------------------------------------------------------+
| GRANT USAGE ON *.* TO `mx_p25_app`@`localhost`              |
| GRANT SELECT ON `mx_p25_db`.* TO `mx_p25_app`@`localhost`   |
| GRANT INSERT ON `mx_p25_db`.`t` TO `mx_p25_app`@`localhost` |
+-------------------------------------------------------------+
3 rows in set (0.00 sec)

现在有三行:USAGE(能登录)、库级 SELECT、表级 INSERT。范围可以叠加,越具体越窄;授权立即生效,不需要重启服务,也不需要 FLUSH PRIVILEGES。

第 3 步:收回那条 INSERT。

输出示例(只读,不要粘贴执行)text不可复制
REVOKE INSERT ON mx_p25_db.t FROM 'mx_p25_app'@'localhost';
MySQL 终端sql
INSERT INTO mx_p25_db.t VALUES (3, 'c');
输出示例(只读,不要粘贴执行)text不可复制
ERROR 1142 (42000): INSERT command denied to user 'mx_p25_app'@'localhost' for table 't'
MySQL 终端sql
SHOW GRANTS;
输出示例(只读,不要粘贴执行)text不可复制
+-----------------------------------------------------------+
| Grants for mx_p25_app@localhost                           |
+-----------------------------------------------------------+
| GRANT USAGE ON *.* TO `mx_p25_app`@`localhost`            |
| GRANT SELECT ON `mx_p25_db`.* TO `mx_p25_app`@`localhost` |
+-----------------------------------------------------------+
2 rows in set (0.00 sec)

又回到两行,写入重新被拒。REVOKE 只收回你点名的那一条,库级 SELECT 不受影响。

说明 再看一眼库级权限的边界:受限账号能查、不能写(隔离实例证据)

同一个 mx_p24_reader 账号,对它有 SELECT 的那张表:

MySQL 终端sql
SELECT COUNT(*) AS 行数 FROM mx_p24_db.t;
输出示例(只读,不要粘贴执行)text不可复制
+--------+
| 行数   |
+--------+
|      1 |
+--------+
1 row in set (0.00 sec)

换成写操作:

MySQL 终端sql
INSERT INTO mx_p24_db.t VALUES (2, 'b');
输出示例(只读,不要粘贴执行)text不可复制
ERROR 1142 (42000): INSERT command denied to user 'mx_p24_reader'@'localhost' for table 't'

1142 和前面的 1044 要分清:1044 是“连这个库的权限都没有”,1142 是“库能进,但这个操作在这张表上没授权”。看到哪个数字,就知道该往哪一层查。共享练习库的 course 账号对 itcast 有读写权限,所以你在那边执行 INSERT 不会被拒——这段只是让你认识错误的形状,不要拿它推断自己的权限。

小结:P26 DCL 小结

账号的完整身份是“用户名@主机”,权限挂在账号上,不挂在用户名上。一条授权 = 权限列表 + 范围,范围越小越安全。日常只做两件事:用 SELECT CURRENT_USER() 确认自己是谁,用 SHOW GRANTS; 确认自己能做什么。建用户、授权、收回都属于管理员动作,本课程不在共享库演示,也不建议给练习账号加权限。记住最小权限:需要查就只给 SELECT,需要写一张表就只给那张表。

常见报错怎么办

看到意思怎么办
1227 ... need the CREATE USER privilege缺少全局管理权限不要在共享库建账号;找管理员,或用你自己的实例
1044 Access denied ... to database对这个库没有任何权限SHOW GRANTS; 看授权范围里有没有这个库
1142 ... command denied ... for table库能进,但这个操作没授权看缺的是 SELECT 还是 INSERT/UPDATE,按最小权限申请

练习

这一节的练习以“读授权”为主,只有前两题需要执行 SQL;后三题用文字回答。

基础 1. 查出当前登录的完整账号(用户名和主机)。

提示 提示

有一个函数直接返回“服务器认证的账号”。

参考答案 答案
MySQL 终端sql
SELECT CURRENT_USER();
输出示例(只读,不要粘贴执行)text不可复制
+-------------------------+
| CURRENT_USER()          |
+-------------------------+
| mx_p24_reader@localhost |
+-------------------------+
1 row in set (0.00 sec)
说明 常见错

用 SELECT USER(); 也能看到账号,但它显示的是“这次连接报的身份”,不是权限所依据的账号。判断权限看 CURRENT_USER()。

基础 2. 读出自己的授权,并指出“能对哪个库、做哪些操作”。

提示 提示

SHOW GRANTS; 的每一行都按“权限 → ON → 范围 → TO → 账号”排列。

参考答案 答案
MySQL 终端sql
SHOW GRANTS;
输出示例(只读,不要粘贴执行)text不可复制
+--------------------------------------------------------------+
| Grants for mx_p24_reader@localhost                           |
+--------------------------------------------------------------+
| GRANT USAGE ON *.* TO `mx_p24_reader`@`localhost`            |
| GRANT SELECT ON `mx_p24_db`.* TO `mx_p24_reader`@`localhost` |
+--------------------------------------------------------------+
2 rows in set (0.00 sec)

以这份输出为例:USAGE ON *.* 只说明能登录;SELECT ON mx_p24_db.* 说明能在 mx_p24_db 的所有表上查询。你在共享库会把 mx_p24_db 读成 itcast。

说明 常见错

只看到 GRANT 就以为“权限很大”。要先看 ON 后面的范围;USAGE 那行没有任何实际操作权限。

**概念 3. GRANT USAGE ON *.* 这一行到底给了什么?**

提示 提示

USAGE 在英文里是“使用”,但在 MySQL 授权里是保留字面的“无权限”。

参考答案 答案

它只表示账号存在、允许登录,不包含任何库或表的操作权限。没有全局权限的账号会自动有这一行;一旦授予全局权限,SHOW GRANTS 就直接列出那条全局权限,不再显示 USAGE。看到它不代表“有全局权限”。

说明 常见错

把 ON *.* 当成“全库权限”。真正决定能不能查、能不能写的是后面那些具体权限名(SELECT、INSERT…)。

**概念 4. 要让同事只能查 itcast 里的一张表,授权应该写到哪一级?**

提示 提示

四层范围:*.*、库.*、库.表、库.表(列)。

参考答案 答案

写到表级,形状是 GRANT SELECT ON itcast.表名 TO '同事'@'主机';——只给 SELECT,范围只到那一张表。这行只是语法示意,不要在共享练习库执行;实际授权由管理员完成。

说明 常见错

图省事写成 ON *.* 或 ON itcast.*,前者等于把整个实例交出去,后者能查整库所有表,都超过“只查一张表”的需要。

**概念 5. 为什么在共享练习库执行 CREATE USER 会得到 1227,而不是语法错误?**

提示 提示

1227 说的是“权限”,1064 才是“语法”。

参考答案 答案

因为语句本身写法没问题,是执行者(course 账号)没有 CREATE USER 这个全局权限。MySQL 先做权限检查,再谈执行;权限不够就报 1227。共享库不开放这个权限,本轮也不申请扩大它。

说明 常见错

以为“多试几次”或“换个写法”就能过。权限检查与写法无关;正确做法是在自己的实例里练,或请管理员操作。

本单元验收

  1. SELECT CURRENT_USER(); 能读出 用户名@主机,并说出 @ 两边各是什么。
  2. SHOW GRANTS; 的输出能逐行读出“权限 + 范围”,并指出哪一行只是 USAGE。
  3. 能区分 1227、1044、1142 分别发生在哪一层。
  4. 能说出最小权限的一个具体做法(例如“只查一张表就写到表级、只给 SELECT”)。
  5. 能说明为什么本页不提供可执行的 CREATE USER/GRANT 语句。

做到这里

做到这里今天可以停。你现在能确认自己是谁、能读懂自己的授权范围,也知道建账号和授权为什么不属于共享练习库的练习。

下一节开始讲函数:先用字符串函数把编号、城市名整理干净,再用数值函数处理小数和余数。

来源与边界

本节对应 BV1Kr4y1i7ru 平台 part 24(原标题「24. 基础-SQL-DCL-用户管理-」)、part 25(「25. 基础-SQL-DCL-权限控制-」)与 part 26(「26. 基础-SQL-DCL小结」,本页以小结承接),主题与同版讲义《MySQL-基础篇》2.7 DCL(p50–53,2.7.1 用户管理 / 2.7.2 权限控制)对应;讲义为公开镜像,仅作事实核对。无逐字稿,未声称听过讲解。

边界说明:读者侧只执行 SELECT CURRENT_USER()、SHOW GRANTS;、SHOW GRANTS FOR CURRENT_USER(); 三条只读语句;CREATE USER / ALTER USER / DROP USER / GRANT / REVOKE 只以 text 语法说明出现,并标明共享练习库不可执行。页面里的 1227 / 1044 / 1142 输出,取自作者隔离的 MySQL 8.0.46 实例:受限账号 mx_p24_reader@localhost 只有 mx_p24_db 的 SELECT,mx_p25_app@localhost 只有 mx_p25_db 的授权;没有 *.*、没有 GRANT OPTION,演练结束后用户与库已在 finally 中删除(构建脚本会断言无残留)。未连接、未修改阿里云学习库,未读取任何凭据,也未在共享库执行任何 DCL 语句。 示例中的 ChangeMe_01 是占位口令,不是真实密码。