第五单元 · 账号与权限只读 · 第 12 / 21 节
账号与权限:谁能连、能做什么
先确认自己是谁(CURRENT_USER 的“用户名@主机”),再逐行读懂 SHOW GRANTS 的“权限 + 范围”,分清 USAGE 只是能登录;然后理解权限的四层范围与最小权限,认识 1227 / 1044 / 1142 各发生在哪一层。读者侧全部只读,建用户与授权只在作者隔离实例演示并注明。
约 18 分钟。这一节不建表、不改数据,只回答两个问题:我现在是哪个账号?这个账号被允许做什么?读者侧能真正执行的只有三条只读语句;建用户、授权这些管理动作,本页只讲语法,并在文字里说明共享练习库为什么会拒绝。
$ / mysql> 提示符和输出。已完成 0 / 2 步
你现在在哪个提示符
应该在 mysql>。断了就先回到学习终端(PowerShell):
ssh root@8.148.204.152
mysql-learning
进去以后确认当前库:
SELECT DATABASE();
+------------+
| DATABASE() |
+------------+
| itcast |
+------------+
1 row in set (0.00 sec)
显示 itcast 就对了。如果是 NULL,执行 USE itcast;——它只切换当前连接用哪个库,不动数据。
第一步:账号是“用户名@主机”
平时登录只输入一个用户名,但在 MySQL 内部,账号的完整身份是 用户名 + 来源主机。同一个用户名从不同机器连进来,会被当成两个不同的账号,权限也各算各的。
先看自己是谁:
SELECT CURRENT_USER(), USER();
+-------------------------+-------------------------+
| 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@… 的形式,主机部分取决于你从哪里连进来——名字不同,读法一样。
再看这个账号被给了什么:
SHOW GRANTS;
+--------------------------------------------------------------+
| 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.* 这样的行,权限列表可能比这里长(练习库要能建表、改数据)。读法完全相同:先看范围,再看权限。
说明 建用户、改密码、删用户的语法(共享练习库没有这个权限,别在这里执行)
下面三行只是语法说明,不要在共享练习库里粘贴执行:
CREATE USER '用户名'@'主机' IDENTIFIED BY '占位口令';
ALTER USER '用户名'@'主机' IDENTIFIED BY '新占位口令';
DROP USER '用户名'@'主机';
- 主机写
'localhost'表示只允许从本机连,写'%'表示任意主机;范围越具体越安全。 IDENTIFIED BY后面是口令。真实环境里不要把口令写进脚本、聊天记录或截图;本页一律用占位串ChangeMe_01。- 建账号是全局管理动作,需要
CREATE USER权限。共享练习库的course账号没有这个权限,本轮也不申请扩大它。
说明 为什么在共享库建用户会被拒:1227 的真实样子
在作者隔离实例里,用同一个受限账号尝试建用户:
CREATE USER 'mx_p24_x'@'localhost' IDENTIFIED BY 'ChangeMe_01';
ERROR 1227 (42000): Access denied; you need (at least one of) the CREATE USER privilege(s) for this operation
1227 的意思是“你没有这个全局权限”,和语法写得对不对无关。同一类错误已在隔离实例复现,未在共享库执行;要建账号,只能由服务器管理员在服务器上做,不属于本课程的练习范围。
同一个受限账号去切到不属于它的库,会被 1044 拦住:
USE itcast;
ERROR 1044 (42000): Access denied for user 'mx_p24_reader'@'localhost' to database 'itcast'
1044 是“对这个库没有任何权限”。共享库的 course 账号不会被拒,因为它的授权范围就是 itcast——这也是为什么你在共享库能直接 USE itcast;。这两个输出都来自隔离实例的受限账号,不是共享库的截图;你在共享库执行 USE itcast; 会成功,正是权限差异的体现。
第二步:权限 = 操作 + 范围,按最小需要给
一条授权由两部分组成:能做什么(权限列表)和对什么做(范围)。范围从大到小有四层:
| 范围写法 | 管到哪里 | 典型用途 |
|---|---|---|
*.* | 整个实例的所有库 | 管理员;普通账号不该有 |
库名.* | 一个库里的所有表 | 只在一个业务库里干活 |
库名.表名 | 一张表 | 只允许碰某一张表 |
库名.表名(列名) | 一张表的某几列 | 只让改某一列,很少用 |
管理授权用 GRANT,收回用 REVOKE,语法形状是:
GRANT 权限列表 ON 范围 TO '用户名'@'主机';
REVOKE 权限列表 ON 范围 FROM '用户名'@'主机';
同样只是语法说明:共享练习库没有授权权限,别在这里执行。GRANT 能成功,前提是执行者自己持有这些权限、并且有 GRANT OPTION;course 账号两者都没有。
读者侧能做的,是把自己的授权读清楚:
SHOW GRANTS FOR CURRENT_USER();
+--------------------------------------------------------------+
| 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。
GRANT SELECT ON mx_p25_db.* TO 'mx_p25_app'@'localhost';
这个账号自己的视角:
SHOW GRANTS;
+-----------------------------------------------------------+
| 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)
SELECT id, v FROM mx_p25_db.t ORDER BY id;
+----+------+
| id | v |
+----+------+
| 1 | a |
+----+------+
1 row in set (0.00 sec)
INSERT INTO mx_p25_db.t VALUES (2, 'b');
ERROR 1142 (42000): INSERT command denied to user 'mx_p25_app'@'localhost' for table 't'
查询可以,写入被拒:1142 是“这个操作在这张表上被拒绝”,比 1044 更具体——库进得去,但 INSERT 这个动作没授权。这就是最小权限:只给工作需要的那一个。
第 2 步:再加一条表级 INSERT。
GRANT INSERT ON mx_p25_db.t TO 'mx_p25_app'@'localhost';
INSERT INTO mx_p25_db.t VALUES (2, 'b');
Query OK, 1 row affected (0.00 sec)
SHOW GRANTS;
+-------------------------------------------------------------+
| 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。
REVOKE INSERT ON mx_p25_db.t FROM 'mx_p25_app'@'localhost';
INSERT INTO mx_p25_db.t VALUES (3, 'c');
ERROR 1142 (42000): INSERT command denied to user 'mx_p25_app'@'localhost' for table 't'
SHOW GRANTS;
+-----------------------------------------------------------+
| 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 的那张表:
SELECT COUNT(*) AS 行数 FROM mx_p24_db.t;
+--------+
| 行数 |
+--------+
| 1 |
+--------+
1 row in set (0.00 sec)
换成写操作:
INSERT INTO mx_p24_db.t VALUES (2, 'b');
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. 查出当前登录的完整账号(用户名和主机)。
提示 提示
有一个函数直接返回“服务器认证的账号”。
参考答案 答案
SELECT CURRENT_USER();
+-------------------------+
| CURRENT_USER() |
+-------------------------+
| mx_p24_reader@localhost |
+-------------------------+
1 row in set (0.00 sec)
说明 常见错
用 SELECT USER(); 也能看到账号,但它显示的是“这次连接报的身份”,不是权限所依据的账号。判断权限看 CURRENT_USER()。
基础 2. 读出自己的授权,并指出“能对哪个库、做哪些操作”。
提示 提示
SHOW GRANTS; 的每一行都按“权限 → ON → 范围 → TO → 账号”排列。
参考答案 答案
SHOW GRANTS;
+--------------------------------------------------------------+
| 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。共享库不开放这个权限,本轮也不申请扩大它。
说明 常见错
以为“多试几次”或“换个写法”就能过。权限检查与写法无关;正确做法是在自己的实例里练,或请管理员操作。
本单元验收
SELECT CURRENT_USER();能读出用户名@主机,并说出@两边各是什么。SHOW GRANTS;的输出能逐行读出“权限 + 范围”,并指出哪一行只是USAGE。- 能区分
1227、1044、1142分别发生在哪一层。 - 能说出最小权限的一个具体做法(例如“只查一张表就写到表级、只给 SELECT”)。
- 能说明为什么本页不提供可执行的
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 是占位口令,不是真实密码。