MySQL 用户、角色与权限管理:MySQL 5.7 与 8.0 对比指南
MySQL 的权限体系主要围绕三个概念展开:
- 用户(User):用于身份认证。
- 权限(Privilege):决定用户可以执行哪些操作。
- 角色(Role) :权限的集合,可批量授予用户。角色是 MySQL 8.0 引入的功能,MySQL 5.7 不支持原生角色。
本文介绍 MySQL 用户、角色和权限的常用操作,并重点说明 MySQL 5.7 与 8.0 的差异。
1. 核心版本差异
| 功能 | MySQL 5.7 | MySQL 8.0 |
|---|---|---|
| 用户与权限分离管理 | 支持,但部分语法较宽松 | 支持,语义更严格 |
CREATE USER |
支持 | 支持 |
GRANT 自动创建用户 |
部分版本和配置下可用,不推荐 | 不支持,必须先创建用户 |
| 原生角色 | 不支持 | 支持 |
| 默认角色 | 不支持 | 支持 |
| 动态权限 | 不支持 | 支持 |
mysql.user 中直接存储权限列 |
是 | 部分全局静态权限仍可见,授权模型更复杂 |
| 默认认证插件 | 通常为 mysql_native_password |
8.0 通常为 caching_sha2_password |
GRANT ... IDENTIFIED BY |
可用于授权并设置密码 | 已移除,应使用 CREATE USER 或 ALTER USER |
SET PASSWORD |
支持 | 支持,但推荐使用 ALTER USER |
SHOW GRANTS |
支持 | 支持,增加角色相关能力 |
| 密码过期、锁定等账号策略 | 部分支持 | 更完善 |
推荐始终将"创建用户"和"授予权限"拆分为两步。这样既兼容 MySQL 8.0,也更便于审计。
2. MySQL 账号的组成
MySQL 账号由用户名和来源主机共同组成:
text
'user_name'@'host_name'
下面三个账号是不同的账号:
sql
'app'@'localhost'
'app'@'192.168.1.%'
'app'@'%'
其中:
'app'@'localhost':仅允许从数据库服务器本机连接。'app'@'192.168.1.%':允许指定网段连接。'app'@'%':允许从任意主机连接。%是通配符,但不等于网络层面已经开放访问。
即使创建了 'app'@'%',远程连接仍可能受到以下因素限制:
bind_address配置;- 防火墙或安全组;
- 数据库端口;
- TLS 配置;
- 网络路由;
- 更精确的同名账号匹配。
安全建议:不要为了方便而默认使用
'user'@'%',应尽量限制来源地址。
3. 用户管理
3.1 创建用户
MySQL 5.7
sql
CREATE USER 'app_user'@'192.168.1.%'
IDENTIFIED BY 'StrongPassword_123!';
MySQL 5.7 中,一些旧式写法允许通过 GRANT 间接创建用户:
sql
GRANT SELECT ON app_db.* TO 'app_user'@'192.168.1.%'
IDENTIFIED BY 'StrongPassword_123!';
这种写法依赖具体版本和 SQL 模式,不建议使用。
推荐拆分为:
sql
CREATE USER 'app_user'@'192.168.1.%'
IDENTIFIED BY 'StrongPassword_123!';
GRANT SELECT ON app_db.* TO 'app_user'@'192.168.1.%';
MySQL 8.0
MySQL 8.0 必须先创建用户,再授予权限:
sql
CREATE USER 'app_user'@'192.168.1.%'
IDENTIFIED BY 'StrongPassword_123!';
GRANT SELECT ON app_db.* TO 'app_user'@'192.168.1.%';
以下旧语法在 MySQL 8.0 中不可用:
sql
GRANT SELECT ON app_db.* TO 'app_user'@'%'
IDENTIFIED BY 'StrongPassword_123!';
3.2 避免用户已存在错误
sql
CREATE USER IF NOT EXISTS 'app_user'@'192.168.1.%'
IDENTIFIED BY 'StrongPassword_123!';
注意:如果用户已经存在,IF NOT EXISTS 不会自动修改其密码。
如需修改密码,应执行:
sql
ALTER USER 'app_user'@'192.168.1.%'
IDENTIFIED BY 'NewStrongPassword_456!';
3.3 指定认证插件
MySQL 5.7 常见写法
sql
CREATE USER 'app_user'@'%'
IDENTIFIED WITH mysql_native_password
BY 'StrongPassword_123!';
MySQL 8.0
MySQL 8.0 默认通常使用 caching_sha2_password:
sql
CREATE USER 'app_user'@'%'
IDENTIFIED WITH caching_sha2_password
BY 'StrongPassword_123!';
在需要兼容不支持 caching_sha2_password 的旧客户端时,部分 MySQL 8.0 版本可以显式使用:
sql
CREATE USER 'legacy_user'@'%'
IDENTIFIED WITH mysql_native_password
BY 'StrongPassword_123!';
但应注意:
mysql_native_password的安全性弱于现代认证方式;- 在较新的 MySQL 8.x 版本中,该插件可能默认禁用或进一步弃用;
- 更推荐升级数据库驱动和客户端,而不是长期依赖旧认证插件。
查看账号认证插件:
sql
SELECT user, host, plugin
FROM mysql.user;
3.4 修改密码
MySQL 5.7 和 8.0 均推荐:
sql
ALTER USER 'app_user'@'192.168.1.%'
IDENTIFIED BY 'NewStrongPassword_456!';
也可以使用:
sql
SET PASSWORD FOR 'app_user'@'192.168.1.%'
= 'NewStrongPassword_456!';
推荐使用 ALTER USER,语义更加清晰,也便于配置账号策略。
3.5 锁定和解锁用户
sql
ALTER USER 'app_user'@'192.168.1.%' ACCOUNT LOCK;
解锁:
sql
ALTER USER 'app_user'@'192.168.1.%' ACCOUNT UNLOCK;
查看锁定状态:
sql
SELECT user, host, account_locked
FROM mysql.user;
MySQL 8.0 对账号锁定、密码过期和登录失败策略提供了更完善的支持。
3.6 密码过期
要求用户下次登录时修改密码:
sql
ALTER USER 'app_user'@'192.168.1.%' PASSWORD EXPIRE;
设置密码永不过期:
sql
ALTER USER 'app_user'@'192.168.1.%'
PASSWORD EXPIRE NEVER;
设置每 90 天过期:
sql
ALTER USER 'app_user'@'192.168.1.%'
PASSWORD EXPIRE INTERVAL 90 DAY;
恢复使用系统默认策略:
sql
ALTER USER 'app_user'@'192.168.1.%'
PASSWORD EXPIRE DEFAULT;
3.7 删除用户
sql
DROP USER 'app_user'@'192.168.1.%';
避免用户不存在时报错:
sql
DROP USER IF EXISTS 'app_user'@'192.168.1.%';
不要直接通过下面的方式删除账号:
sql
DELETE FROM mysql.user
WHERE user = 'app_user';
直接修改系统表可能导致权限数据不一致,应使用官方账号管理语句。
3.8 重命名用户
sql
RENAME USER
'app_user'@'192.168.1.%'
TO
'new_app_user'@'192.168.1.%';
4. MySQL 权限层级
MySQL 权限可以作用在多个层级。
| 层级 | 授权对象示例 | 说明 |
|---|---|---|
| 全局级 | *.* |
对所有数据库生效 |
| 数据库级 | app_db.* |
对指定数据库中的所有对象生效 |
| 表级 | app_db.orders |
对指定表生效 |
| 列级 | app_db.users(name, email) |
对指定列生效 |
| 存储过程级 | PROCEDURE app_db.p_name |
对存储过程生效 |
| 函数级 | FUNCTION app_db.f_name |
对存储函数生效 |
授权范围越大,风险通常越高。应遵循最小权限原则。
5. 常用权限
5.1 数据操作权限
| 权限 | 说明 |
|---|---|
SELECT |
查询数据 |
INSERT |
插入数据 |
UPDATE |
更新数据 |
DELETE |
删除数据 |
EXECUTE |
执行存储过程或函数 |
5.2 结构管理权限
| 权限 | 说明 |
|---|---|
CREATE |
创建数据库或对象 |
ALTER |
修改表或对象结构 |
DROP |
删除数据库或对象 |
INDEX |
创建或删除索引 |
CREATE VIEW |
创建视图 |
SHOW VIEW |
查看视图定义 |
TRIGGER |
创建或操作触发器 |
EVENT |
管理事件调度器 |
5.3 管理类权限
常见权限包括:
PROCESSRELOADSHUTDOWNREPLICATION CLIENTREPLICATION SLAVECREATE USERGRANT OPTIONSUPER
其中 SUPER 权限范围较大。在 MySQL 8.0 中,其部分能力被拆分为更细粒度的动态权限,应优先授予所需的具体权限。
6. 授予权限
6.1 授予数据库级查询权限
sql
GRANT SELECT
ON app_db.*
TO 'app_user'@'192.168.1.%';
6.2 授予增删改查权限
sql
GRANT SELECT, INSERT, UPDATE, DELETE
ON app_db.*
TO 'app_user'@'192.168.1.%';
6.3 授予表级权限
sql
GRANT SELECT, UPDATE
ON app_db.orders
TO 'app_user'@'192.168.1.%';
6.4 授予列级权限
sql
GRANT SELECT (id, username, email),
UPDATE (email)
ON app_db.users
TO 'app_user'@'192.168.1.%';
这表示用户可以:
- 查询
id、username、email; - 仅更新
email列。
列级授权较精细,但也会增加维护成本。
6.5 授予存储过程执行权限
sql
GRANT EXECUTE
ON PROCEDURE app_db.create_order
TO 'app_user'@'192.168.1.%';
存储函数:
sql
GRANT EXECUTE
ON FUNCTION app_db.calculate_price
TO 'app_user'@'192.168.1.%';
6.6 授予所有权限
授予某个数据库的所有权限:
sql
GRANT ALL PRIVILEGES
ON app_db.*
TO 'app_admin'@'192.168.1.%';
授予全局所有权限:
sql
GRANT ALL PRIVILEGES
ON *.*
TO 'super_admin'@'localhost';
ALL PRIVILEGES ON *.*权限极高,生产环境中应谨慎使用。
6.7 WITH GRANT OPTION
允许用户将自己拥有的权限继续授予其他账号:
sql
GRANT SELECT, INSERT
ON app_db.*
TO 'team_admin'@'192.168.1.%'
WITH GRANT OPTION;
这会扩大权限传播范围。除非账号确实承担权限管理职责,否则不应授予。
撤销授权传播能力:
sql
REVOKE GRANT OPTION
ON app_db.*
FROM 'team_admin'@'192.168.1.%';
7. 查看权限
查看当前账号权限:
sql
SHOW GRANTS;
查看指定账号权限:
sql
SHOW GRANTS FOR 'app_user'@'192.168.1.%';
输出可能类似:
sql
GRANT USAGE ON *.* TO 'app_user'@'192.168.1.%';
GRANT SELECT, INSERT, UPDATE, DELETE
ON `app_db`.*
TO 'app_user'@'192.168.1.%';
其中 USAGE 通常表示账号存在,但该授权项没有赋予额外业务权限。
查看当前会话匹配到的账号:
sql
SELECT USER(), CURRENT_USER();
区别如下:
USER():客户端登录时提交的用户名和主机信息;CURRENT_USER():MySQL 权限匹配后实际使用的账号。
排查"明明授权却没有权限"等问题时,CURRENT_USER() 非常重要。
8. 撤销权限
撤销某项权限:
sql
REVOKE INSERT, UPDATE
ON app_db.*
FROM 'app_user'@'192.168.1.%';
撤销指定范围内的全部权限:
sql
REVOKE ALL PRIVILEGES
ON app_db.*
FROM 'app_user'@'192.168.1.%';
撤销账号拥有的全部权限和授权能力:
sql
REVOKE ALL PRIVILEGES, GRANT OPTION
FROM 'app_user'@'192.168.1.%';
注意:
REVOKE只移除权限,不会删除用户;- 删除用户应使用
DROP USER。
9. 是否需要执行 FLUSH PRIVILEGES
使用下列标准语句管理权限时,通常不需要 执行 FLUSH PRIVILEGES:
sql
CREATE USER
ALTER USER
DROP USER
GRANT
REVOKE
MySQL 会立即重新加载相关授权信息。
只有在直接修改授权系统表后,才可能需要:
sql
FLUSH PRIVILEGES;
但不建议直接修改 mysql.user 等系统表。
10. MySQL 8.0 角色管理
角色是 MySQL 8.0 的重要能力。角色本质上是一个命名的权限集合。
例如,可按业务职责创建:
- 只读角色;
- 读写角色;
- 数据库管理员角色;
- 审计角色。
MySQL 5.7 不支持下面的原生角色语法。
10.1 创建角色
sql
CREATE ROLE 'app_readonly', 'app_readwrite';
为了避免命名歧义,也可以明确指定主机部分:
sql
CREATE ROLE 'app_readonly'@'%';
实际使用时建议团队统一角色命名和主机部分。
10.2 给角色授予权限
为只读角色授权:
sql
GRANT SELECT
ON app_db.*
TO 'app_readonly';
为读写角色授权:
sql
GRANT SELECT, INSERT, UPDATE, DELETE
ON app_db.*
TO 'app_readwrite';
10.3 将角色授予用户
先创建用户:
sql
CREATE USER 'alice'@'%'
IDENTIFIED BY 'StrongPassword_123!';
授予角色:
sql
GRANT 'app_readonly' TO 'alice'@'%';
一个用户可以拥有多个角色:
sql
GRANT 'app_readonly', 'report_role'
TO 'alice'@'%';
需要注意:
将角色授予用户后,角色不一定自动在会话中生效,还需要激活角色或设置默认角色。
10.4 设置默认角色
设置指定默认角色:
sql
SET DEFAULT ROLE 'app_readonly'
TO 'alice'@'%';
将用户拥有的全部角色设置为默认角色:
sql
SET DEFAULT ROLE ALL
TO 'alice'@'%';
取消默认角色:
sql
SET DEFAULT ROLE NONE
TO 'alice'@'%';
默认角色会在用户登录时自动激活。
10.5 在当前会话中激活角色
激活全部已授予角色:
sql
SET ROLE ALL;
激活指定角色:
sql
SET ROLE 'app_readonly';
停用所有角色:
sql
SET ROLE NONE;
使用默认角色:
sql
SET ROLE DEFAULT;
查看当前激活的角色:
sql
SELECT CURRENT_ROLE();
10.6 查看角色授权
sql
SHOW GRANTS FOR 'alice'@'%';
查看某个角色的权限:
sql
SHOW GRANTS FOR 'app_readonly';
在 MySQL 8.0 中,还可以根据版本和场景使用 USING 查看角色生效后的授权结果:
sql
SHOW GRANTS FOR 'alice'@'%'
USING 'app_readonly';
10.7 撤销角色
从用户撤销角色:
sql
REVOKE 'app_readonly'
FROM 'alice'@'%';
删除角色:
sql
DROP ROLE 'app_readonly';
删除角色不会删除被授予该角色的用户,但角色提供的权限将不再可用。
10.8 角色继承
MySQL 8.0 支持将一个角色授予另一个角色:
sql
CREATE ROLE 'base_reader', 'business_operator';
GRANT SELECT
ON app_db.*
TO 'base_reader';
GRANT 'base_reader'
TO 'business_operator';
GRANT INSERT, UPDATE
ON app_db.*
TO 'business_operator';
此时 business_operator 同时拥有:
- 自身的
INSERT、UPDATE; - 从
base_reader继承的SELECT。
设计角色继承时应避免复杂甚至循环的授权关系,防止权限难以审计。
11. MySQL 5.7 如何模拟角色
MySQL 5.7 没有原生角色能力,不能使用:
sql
CREATE ROLE ...
GRANT role_name TO user_name ...
SET ROLE ...
通常可采用以下替代方案。
11.1 使用标准化授权脚本
只读用户模板:
sql
CREATE USER 'alice'@'%'
IDENTIFIED BY 'StrongPassword_123!';
GRANT SELECT
ON app_db.*
TO 'alice'@'%';
读写用户模板:
sql
CREATE USER 'bob'@'%'
IDENTIFIED BY 'StrongPassword_456!';
GRANT SELECT, INSERT, UPDATE, DELETE
ON app_db.*
TO 'bob'@'%';
将这些脚本纳入:
- Git 版本管理;
- 数据库变更平台;
- 自动化运维工具;
- 审批流程。
这样可以在管理层面模拟角色。
11.2 使用配置管理工具
可使用自动化工具统一维护账号与权限,例如:
- Ansible;
- Terraform 相关 Provider;
- 自研权限管理平台;
- 数据库运维平台。
建议将"角色名称"映射为一组固定权限,再由工具对每个用户执行相同授权。
11.3 不建议使用"共享账号"模拟角色
例如,让所有只读人员共同使用:
text
readonly@%
这种方式虽然看似简单,但存在明显问题:
- 无法追踪具体操作人员;
- 密码泄露后影响范围大;
- 人员离职时不便单独撤销;
- 审计和合规困难。
正确方式是"一人一账号",再通过脚本或平台统一分配权限。
12. MySQL 8.0 动态权限
MySQL 5.7 中许多管理能力集中在 SUPER 等静态权限中。MySQL 8.0 引入动态权限,将部分高风险管理能力拆分得更细。
常见动态权限可能包括:
text
BACKUP_ADMIN
CONNECTION_ADMIN
REPLICATION_APPLIER
REPLICATION_SLAVE_ADMIN
SYSTEM_VARIABLES_ADMIN
SESSION_VARIABLES_ADMIN
BINLOG_ADMIN
CLONE_ADMIN
RESOURCE_GROUP_ADMIN
ROLE_ADMIN
具体可用权限与 MySQL 8.0 的小版本、已安装组件和插件有关。
授予动态权限的示例:
sql
GRANT BACKUP_ADMIN
ON *.*
TO 'backup_user'@'localhost';
撤销:
sql
REVOKE BACKUP_ADMIN
ON *.*
FROM 'backup_user'@'localhost';
查看服务器支持的动态权限:
sql
SELECT PRIVILEGE_TYPE
FROM INFORMATION_SCHEMA.USER_PRIVILEGES
WHERE GRANTEE = '''root''@''localhost''';
也可以结合以下语句检查具体账号:
sql
SHOW GRANTS FOR 'backup_user'@'localhost';
在 MySQL 8.0 中,应尽量使用细粒度动态权限代替范围过大的
SUPER权限。
13. 权限信息存储与查询
13.1 常见系统表
MySQL 的授权信息存储在 mysql 系统库中,常见表包括:
text
mysql.user
mysql.db
mysql.tables_priv
mysql.columns_priv
mysql.procs_priv
MySQL 8.0 还包含角色相关授权信息,例如:
text
mysql.role_edges
mysql.default_roles
不建议应用程序依赖这些系统表的具体字段结构,因为不同版本可能发生变化。
13.2 推荐使用 INFORMATION_SCHEMA
查看全局权限:
sql
SELECT *
FROM INFORMATION_SCHEMA.USER_PRIVILEGES;
查看数据库级权限:
sql
SELECT *
FROM INFORMATION_SCHEMA.SCHEMA_PRIVILEGES;
查看表级权限:
sql
SELECT *
FROM INFORMATION_SCHEMA.TABLE_PRIVILEGES;
查看列级权限:
sql
SELECT *
FROM INFORMATION_SCHEMA.COLUMN_PRIVILEGES;
审计单个账号时,通常优先使用:
sql
SHOW GRANTS FOR 'app_user'@'%';
14. DEFINER 与权限
视图、触发器、事件、存储过程和函数可能包含 DEFINER:
sql
CREATE DEFINER='admin'@'localhost'
VIEW app_db.v_orders AS
SELECT * FROM app_db.orders;
迁移数据库时,如果目标实例不存在对应的 DEFINER 账号,可能出现:
- 对象创建失败;
- 对象执行失败;
- 权限行为与预期不一致。
查看视图定义:
sql
SHOW CREATE VIEW app_db.v_orders;
查看存储过程定义:
sql
SHOW CREATE PROCEDURE app_db.some_procedure;
数据库迁移时应将 DEFINER 纳入检查范围。
15. 典型授权方案
15.1 只读账号
MySQL 5.7
sql
CREATE USER 'report_user'@'10.0.0.%'
IDENTIFIED BY 'StrongPassword_123!';
GRANT SELECT, SHOW VIEW
ON report_db.*
TO 'report_user'@'10.0.0.%';
MySQL 8.0:使用角色
sql
CREATE ROLE 'report_reader';
GRANT SELECT, SHOW VIEW
ON report_db.*
TO 'report_reader';
CREATE USER 'report_user'@'10.0.0.%'
IDENTIFIED BY 'StrongPassword_123!';
GRANT 'report_reader'
TO 'report_user'@'10.0.0.%';
SET DEFAULT ROLE 'report_reader'
TO 'report_user'@'10.0.0.%';
15.2 应用读写账号
sql
CREATE USER 'order_service'@'10.0.1.%'
IDENTIFIED BY 'StrongPassword_456!';
GRANT SELECT, INSERT, UPDATE, DELETE
ON order_db.*
TO 'order_service'@'10.0.1.%';
应用账号通常不应拥有:
text
DROP
ALTER
CREATE USER
GRANT OPTION
SHUTDOWN
SUPER
如果应用负责自动建表,应单独评估 CREATE、ALTER、INDEX 等权限,最好将数据库变更交给独立发布流程。
15.3 数据库变更账号
sql
CREATE USER 'migration_user'@'10.0.2.%'
IDENTIFIED BY 'StrongPassword_789!';
GRANT SELECT, INSERT, UPDATE, DELETE,
CREATE, ALTER, DROP, INDEX,
CREATE VIEW, SHOW VIEW, TRIGGER
ON order_db.*
TO 'migration_user'@'10.0.2.%';
该账号应仅在发布期间使用,并通过密钥管理系统安全保存凭据。
15.4 备份账号
备份工具所需权限取决于:
- 逻辑备份还是物理备份;
- MySQL 版本;
- 使用的备份工具;
- 是否需要锁表;
- 是否备份存储过程、事件和触发器。
一个基础逻辑备份示例:
sql
CREATE USER 'backup_user'@'localhost'
IDENTIFIED BY 'StrongPassword_Backup!';
GRANT SELECT, SHOW VIEW, TRIGGER, EVENT,
LOCK TABLES
ON *.*
TO 'backup_user'@'localhost';
MySQL 8.0 中,某些备份工具还可能需要:
sql
GRANT BACKUP_ADMIN
ON *.*
TO 'backup_user'@'localhost';
不要机械复制备份权限模板,应以实际工具文档为准。
15.5 复制账号
MySQL 5.7 常见方式
sql
CREATE USER 'repl_user'@'10.0.3.%'
IDENTIFIED BY 'StrongPassword_Repl!';
GRANT REPLICATION SLAVE
ON *.*
TO 'repl_user'@'10.0.3.%';
MySQL 8.0
传统权限仍可能使用,但较新的术语和动态权限体系逐步替代部分旧命名。应根据 MySQL 8.0 的具体小版本和复制方案确定权限。
16. 常见误区
16.1 误以为 GRANT 总能创建用户
MySQL 5.7 的部分环境中,旧式 GRANT ... IDENTIFIED BY 可能创建用户;MySQL 8.0 不再支持。
统一使用:
sql
CREATE USER ...;
GRANT ...;
16.2 给了角色但权限没有生效
MySQL 8.0 中,角色可能尚未激活。
检查:
sql
SELECT CURRENT_ROLE();
激活:
sql
SET ROLE ALL;
或设置默认角色:
sql
SET DEFAULT ROLE ALL TO 'alice'@'%';
16.3 授权后反复执行 FLUSH PRIVILEGES
正常使用 GRANT、REVOKE、CREATE USER 等语句时,不需要执行。
16.4 只按用户名授权,忽略主机
下面是两个不同账号:
sql
'app'@'localhost'
'app'@'%'
如果实际连接匹配了 'app'@'localhost',给 'app'@'%' 授权不一定能解决问题。
应检查:
sql
SELECT USER(), CURRENT_USER();
16.5 直接查询 mysql.user 判断全部权限
这在 MySQL 8.0 中尤其不可靠,因为:
- 权限可能来自数据库级、表级或列级授权;
- 权限可能来自角色;
- 权限可能是动态权限;
- 系统表结构随版本变化。
应优先使用:
sql
SHOW GRANTS;
以及 INFORMATION_SCHEMA 中的权限视图。
16.6 给应用账号授予 ALL PRIVILEGES
应用通常只需要:
text
SELECT
INSERT
UPDATE
DELETE
某些应用可能还需要:
text
EXECUTE
不应默认授予数据库结构管理或全局管理权限。
16.7 误以为用户名相同就是同一个用户
MySQL 账号由 user 和 host 共同唯一标识:
sql
'user'@'localhost'
'user'@'%'
'user'@'10.0.0.%'
对其中一个账号执行 ALTER USER 或 GRANT,不会自动影响其他账号。
17. 权限排查步骤
当用户报告"没有权限"时,可以按以下步骤排查。
17.1 确认当前账号匹配结果
sql
SELECT USER(), CURRENT_USER();
17.2 查看直接授权
sql
SHOW GRANTS FOR 'app_user'@'%';
17.3 MySQL 8.0 检查角色
sql
SELECT CURRENT_ROLE();
必要时:
sql
SET ROLE ALL;
17.4 检查授权范围
确认权限授予的是:
text
*.*
app_db.*
app_db.some_table
数据库名和表名应与实际访问对象一致。
17.5 检查对象的 DEFINER 和执行安全模式
存储程序和视图还可能受到:
SQL SECURITY DEFINERSQL SECURITY INVOKERDEFINER
影响。
17.6 检查事务和连接池
权限修改通常会立即生效,但应用连接池可能:
- 长期复用旧连接;
- 使用了另一个数据源;
- 使用了与预期不同的账号;
- 连接到了其他实例。
18. 安全最佳实践
18.1 遵循最小权限原则
只授予完成工作所需的最小权限:
sql
GRANT SELECT, INSERT, UPDATE, DELETE
ON app_db.*
TO 'app_user'@'10.0.1.%';
而不是:
sql
GRANT ALL PRIVILEGES
ON *.*
TO 'app_user'@'%';
18.2 限制来源主机
优先使用:
sql
'app_user'@'10.0.1.%'
而不是:
sql
'app_user'@'%'
更高安全要求下,可限制到具体 IP。
18.3 区分账号用途
建议至少拆分:
- 应用运行账号;
- 数据库变更账号;
- 只读查询账号;
- 备份账号;
- 复制账号;
- 运维管理员账号。
不要让应用与管理员共用账号。
18.4 MySQL 8.0 优先使用角色
建议:
text
用户 → 角色 → 权限
而不是为每个用户重复维护大量权限。
角色命名可以采用:
text
业务名_环境_职责
例如:
text
order_prod_reader
order_prod_writer
order_prod_migration
18.5 定期审计
定期检查:
- 长期未登录账号;
- 使用
%的账号; - 拥有
GRANT OPTION的账号; - 拥有全局权限的账号;
- 拥有
ALL PRIVILEGES的账号; - 密码永不过期的账号;
- 已离职人员账号;
- MySQL 8.0 中未使用或过度授权的角色。
18.6 不在代码中明文保存密码
应使用:
- 环境变量;
- 密钥管理服务;
- Kubernetes Secret;
- Vault;
- 云厂商 Secret Manager;
- 受控配置中心。
同时限制凭据读取权限并定期轮换密码。
19. MySQL 5.7 到 8.0 的迁移注意事项
从 MySQL 5.7 升级到 MySQL 8.0 时,应重点检查以下内容。
19.1 GRANT ... IDENTIFIED BY
旧脚本:
sql
GRANT SELECT ON app_db.*
TO 'app_user'@'%'
IDENTIFIED BY 'Password';
应改为:
sql
CREATE USER 'app_user'@'%'
IDENTIFIED BY 'Password';
GRANT SELECT ON app_db.*
TO 'app_user'@'%';
19.2 认证插件兼容性
旧客户端可能不支持 MySQL 8.0 默认的 caching_sha2_password。
推荐方案:
- 升级客户端或数据库驱动;
- 验证 TLS 和认证流程;
- 只有无法升级时,才临时考虑旧认证插件。
19.3 SUPER 权限拆分
原来依赖 SUPER 的运维脚本,在 MySQL 8.0 中可能需要改为具体动态权限。
迁移前应逐项核对脚本执行的操作,而不是简单地继续授予高权限。
19.4 引入角色
升级到 MySQL 8.0 后,可将大量重复授权整理为角色。
例如原来有 100 个只读用户,每个都单独执行:
sql
GRANT SELECT ON app_db.* TO ...;
可以改为:
sql
CREATE ROLE 'app_reader';
GRANT SELECT
ON app_db.*
TO 'app_reader';
然后将角色授予用户并设置为默认角色。
19.5 不要直接迁移系统授权表
不要通过复制 MySQL 5.7 的 mysql.user 等系统表来迁移账号。
应使用:
- 官方升级流程;
- 逻辑导出的用户创建和授权语句;
SHOW GRANTS生成的授权脚本;- 经验证的账号迁移工具。
MySQL 5.7 和 8.0 的系统表结构及授权模型不同,直接复制可能破坏系统权限数据。
20. 完整示例
20.1 MySQL 5.7:创建只读和读写用户
sql
CREATE DATABASE app_db;
CREATE USER 'app_reader'@'10.0.0.%'
IDENTIFIED BY 'ReaderPassword_123!';
GRANT SELECT, SHOW VIEW
ON app_db.*
TO 'app_reader'@'10.0.0.%';
CREATE USER 'app_writer'@'10.0.0.%'
IDENTIFIED BY 'WriterPassword_456!';
GRANT SELECT, INSERT, UPDATE, DELETE
ON app_db.*
TO 'app_writer'@'10.0.0.%';
SHOW GRANTS FOR 'app_reader'@'10.0.0.%';
SHOW GRANTS FOR 'app_writer'@'10.0.0.%';
20.2 MySQL 8.0:使用角色管理权限
sql
CREATE DATABASE app_db;
CREATE ROLE 'app_reader_role', 'app_writer_role';
GRANT SELECT, SHOW VIEW
ON app_db.*
TO 'app_reader_role';
GRANT SELECT, INSERT, UPDATE, DELETE
ON app_db.*
TO 'app_writer_role';
CREATE USER 'alice'@'10.0.0.%'
IDENTIFIED BY 'AlicePassword_123!';
CREATE USER 'bob'@'10.0.0.%'
IDENTIFIED BY 'BobPassword_456!';
GRANT 'app_reader_role'
TO 'alice'@'10.0.0.%';
GRANT 'app_writer_role'
TO 'bob'@'10.0.0.%';
SET DEFAULT ROLE 'app_reader_role'
TO 'alice'@'10.0.0.%';
SET DEFAULT ROLE 'app_writer_role'
TO 'bob'@'10.0.0.%';
SHOW GRANTS FOR 'alice'@'10.0.0.%';
SHOW GRANTS FOR 'bob'@'10.0.0.%';
用户登录后可以检查:
sql
SELECT CURRENT_ROLE();
21. 常用命令速查
用户管理
sql
CREATE USER 'user'@'host' IDENTIFIED BY 'password';
ALTER USER 'user'@'host' IDENTIFIED BY 'new_password';
ALTER USER 'user'@'host' ACCOUNT LOCK;
ALTER USER 'user'@'host' ACCOUNT UNLOCK;
DROP USER 'user'@'host';
RENAME USER 'old'@'host' TO 'new'@'host';
权限管理
sql
GRANT SELECT ON db_name.* TO 'user'@'host';
GRANT SELECT, INSERT, UPDATE, DELETE
ON db_name.*
TO 'user'@'host';
REVOKE UPDATE
ON db_name.*
FROM 'user'@'host';
SHOW GRANTS FOR 'user'@'host';
MySQL 8.0 角色管理
sql
CREATE ROLE 'role_name';
GRANT SELECT
ON db_name.*
TO 'role_name';
GRANT 'role_name'
TO 'user'@'host';
SET DEFAULT ROLE 'role_name'
TO 'user'@'host';
SET ROLE ALL;
SELECT CURRENT_ROLE();
REVOKE 'role_name'
FROM 'user'@'host';
DROP ROLE 'role_name';
总结
MySQL 5.7 与 8.0 在用户和权限管理方面的主要区别是:
- MySQL 8.0 不再允许通过
GRANT ... IDENTIFIED BY创建用户 ,应先CREATE USER,再GRANT。 - MySQL 8.0 原生支持角色,可实现"用户---角色---权限"的分层管理。
- MySQL 8.0 引入动态权限 ,将部分高风险管理能力从
SUPER中拆分出来。 - MySQL 8.0 默认认证方式发生变化,旧客户端可能需要升级。
- MySQL 5.7 没有原生角色,通常通过授权模板和自动化平台模拟。
- 不论使用哪个版本,都应遵循最小权限、限制来源、一人一账号、定期审计等安全原则。