补充:权限与角色

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 USERALTER 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 管理类权限

常见权限包括:

  • PROCESS
  • RELOAD
  • SHUTDOWN
  • REPLICATION CLIENT
  • REPLICATION SLAVE
  • CREATE USER
  • GRANT OPTION
  • SUPER

其中 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.%';

这表示用户可以:

  • 查询 idusernameemail
  • 仅更新 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 同时拥有:

  • 自身的 INSERTUPDATE
  • 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

如果应用负责自动建表,应单独评估 CREATEALTERINDEX 等权限,最好将数据库变更交给独立发布流程。


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

正常使用 GRANTREVOKECREATE 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 账号由 userhost 共同唯一标识:

sql 复制代码
'user'@'localhost'
'user'@'%'
'user'@'10.0.0.%'

对其中一个账号执行 ALTER USERGRANT,不会自动影响其他账号。


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 DEFINER
  • SQL SECURITY INVOKER
  • DEFINER

影响。

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

推荐方案:

  1. 升级客户端或数据库驱动;
  2. 验证 TLS 和认证流程;
  3. 只有无法升级时,才临时考虑旧认证插件。

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 在用户和权限管理方面的主要区别是:

  1. MySQL 8.0 不再允许通过 GRANT ... IDENTIFIED BY 创建用户 ,应先 CREATE USER,再 GRANT
  2. MySQL 8.0 原生支持角色,可实现"用户---角色---权限"的分层管理。
  3. MySQL 8.0 引入动态权限 ,将部分高风险管理能力从 SUPER 中拆分出来。
  4. MySQL 8.0 默认认证方式发生变化,旧客户端可能需要升级。
  5. MySQL 5.7 没有原生角色,通常通过授权模板和自动化平台模拟。
  6. 不论使用哪个版本,都应遵循最小权限、限制来源、一人一账号、定期审计等安全原则。
相关推荐
蒲锘2 小时前
DevOps 实验阶段四:Infra 节点 + MySQL + Spring Boot 容器化
spring boot·mysql·docker
呆呆小孩12 小时前
零基础学 MySQL(上):从建库建表到增删改查,一篇搭好 SQL 基础
android·sql·mysql
我不会插花弄玉14 小时前
2.库的操作【由浅入深-MySQL】
数据库·mysql
King of fraud17 小时前
Linux 下 MySQL 基础操作:创建用户、数据库与权限管理实战
linux·数据库·mysql
这个DBA有点耶17 小时前
Redis大key“根治”指南:拆分策略与数据结构选型
数据库·mysql·架构
__zRainy__20 小时前
Node系列 · ORM:mysql 驱动程序
数据库·后端·mysql·node.js·orm
痕迹运维20 小时前
MySQL8 Docker容器化部署之C86架构(海光CPU)
mysql
程序员夏洛20 小时前
MySQL 中如果发生死锁应该如何解决?
数据库·mysql
破土士V21 小时前
【MySQL基础知识】数据库约束&联合查询&索引
数据库·mysql·索引·联合查询·数据库约束