引言
MySQL 8.0 在权限管理方面引入了多项改进,包括角色(Role) 、动态权限 、密码过期策略等特性,使权限管理更加灵活和安全。本文基于 MySQL 8.0 语法,结合实际场景,详细介绍如何为数据库、表赋予只读、读写等精确权限。
权限体系原理图
权限检查流程

继承关系

MySQL 8.0 核心变化
| 特性 | MySQL 5.7 及以前 | MySQL 8.0 |
|---|---|---|
| 创建用户 | GRANT ... IDENTIFIED BY |
必须先 CREATE USER |
| 角色支持 | 不支持 | 支持 CREATE ROLE |
| 密码管理 | 基础功能 | 支持过期、历史记录、重试限制 |
| 动态权限 | 固定权限集 | 可扩展(如 BINLOG_ADMIN) |
| 账户锁定 | 不支持 | ACCOUNT LOCK/UNLOCK |
| 认证插件 | mysql_native_password 默认 |
caching_sha2_password 默认 |
核心权限类型(MySQL 8.0)
| 权限 | 说明 | 适用场景 |
|---|---|---|
SELECT |
读取数据 | 只读用户 |
INSERT |
插入数据 | 数据录入 |
UPDATE |
更新数据 | 数据修改 |
DELETE |
删除数据 | 数据清理 |
CREATE |
创建表/库 | 开发环境 |
DROP |
删除表/库 | 管理员 |
ALTER |
修改表结构 | DDL 变更 |
INDEX |
创建/删除索引 | 性能优化 |
EXECUTE |
执行存储过程 | 应用账号 |
CREATE ROLE |
创建角色 | 权限管理员 |
ALL PRIVILEGES |
所有权限(除 GRANT) | 完全控制 |
语法基础(MySQL 8.0)
sql
-- 创建用户(8.0 必须先创建)
CREATE USER 'username'@'host'
IDENTIFIED BY 'password'
[PASSWORD EXPIRE [DEFAULT|NEVER|INTERVAL N DAY]]
[ACCOUNT LOCK | UNLOCK];
-- 授予权限
GRANT privilege_type [(column_list)]
ON [object_type] privilege_level
TO 'username'@'host'
[WITH GRANT OPTION];
-- 回收权限
REVOKE privilege_type
ON privilege_level
FROM 'username'@'host';
-- 查看权限
SHOW GRANTS FOR 'username'@'host';
-- 角色操作(8.0 新增)
CREATE ROLE 'role_name';
GRANT 'role_name' TO 'username'@'host';
SET DEFAULT ROLE 'role_name' TO 'username'@'host';
-- 权限立即生效(8.0 无需 FLUSH PRIVILEGES,但仍建议执行)
FLUSH PRIVILEGES;
使用场景与命令
场景一:只读用户 - 查询任意表
需求 :为数据分析师 analyst 创建账号,只能查询 sales_db 数据库的所有表,不能做任何修改。
sql
-- 创建用户(MySQL 8.0 语法)
CREATE USER 'analyst'@'192.168.1.%'
IDENTIFIED BY 'ReadOnly@2024'
PASSWORD EXPIRE INTERVAL 90 DAY;
-- 授予数据库级只读权限
GRANT SELECT ON sales_db.* TO 'analyst'@'192.168.1.%';
-- 验证
SHOW GRANTS FOR 'analyst'@'192.168.1.%';
权限决策流程图:

场景二:使用角色(Role)管理权限组
需求:多个只读用户需要相同权限,使用角色统一管理。
sql
-- 创建角色(MySQL 8.0 核心特性)
CREATE ROLE 'readonly_role', 'readwrite_role';
-- 为角色授予权限
GRANT SELECT ON sales_db.* TO 'readonly_role';
GRANT SELECT, INSERT, UPDATE, DELETE ON sales_db.* TO 'readwrite_role';
-- 创建用户并赋予角色
CREATE USER 'alice'@'%' IDENTIFIED BY 'Alice@2024';
CREATE USER 'bob'@'%' IDENTIFIED BY 'Bob@2024';
-- 分配角色
GRANT 'readonly_role' TO 'alice'@'%';
GRANT 'readwrite_role' TO 'bob'@'%';
-- 激活角色(默认激活或设置默认角色)
SET DEFAULT ROLE 'readonly_role' TO 'alice'@'%';
SET DEFAULT ROLE 'readwrite_role' TO 'bob'@'%';
-- 查看角色权限
SHOW GRANTS FOR 'alice'@'%' USING 'readonly_role';
角色管理架构图:

场景三:只读用户 - 限制只能查特定表
需求 :外包人员只能查询 sales_db.orders 和 sales_db.products 两张表。
sql
CREATE USER 'outsource'@'10.0.0.%'
IDENTIFIED BY 'Out@2024'
PASSWORD EXPIRE DEFAULT
FAILED_LOGIN_ATTEMPTS 3
PASSWORD_LOCK_TIME 2;
-- 表级授权
GRANT SELECT ON sales_db.orders TO 'outsource'@'10.0.0.%';
GRANT SELECT ON sales_db.products TO 'outsource'@'10.0.0.%';
-- 撤销全局数据库的默认权限
REVOKE ALL PRIVILEGES ON sales_db.* FROM 'outsource'@'10.0.0.%';
场景四:读写用户 - 允许增删改查
需求 :测试人员 tester 需要 test_db 库的完整读写能力,但不能修改表结构。
sql
CREATE USER 'tester'@'%'
IDENTIFIED BY 'Test@2024'
WITH MAX_QUERIES_PER_HOUR 10000;
-- 授予数据操作权限(不含 DDL)
GRANT SELECT, INSERT, UPDATE, DELETE ON test_db.* TO 'tester'@'%';
-- 禁止 DDL 操作(通过不授权实现)
-- 如果用户尝试 ALTER TABLE,将收到 ERROR 1142
场景五:动态权限使用(MySQL 8.0 新增)
需求 :备份账号需要 LOCK TABLES 和 SELECT 权限,但不需其他管理权限。
sql
-- MySQL 8.0 动态权限示例
CREATE USER 'backup_user'@'localhost' IDENTIFIED BY 'Backup@2024';
-- 只授予备份所需的最小权限集
GRANT SELECT, LOCK TABLES, SHOW VIEW ON mydb.* TO 'backup_user'@'localhost';
-- 对于二进制日志管理,使用动态权限(代替 SUPER)
GRANT BINLOG_ADMIN, REPLICATION_SLAVE_ADMIN ON *.* TO 'repl_user'@'%';
场景六:列级权限 + 视图替代方案
需求 :客服人员只能查询 users 表的 name 和 email,不能查看敏感字段。
sql
-- 方案一:直接列授权(不推荐,管理复杂)
GRANT SELECT (name, email) ON app_db.users TO 'support'@'%';
-- 方案二:创建视图(推荐)
CREATE VIEW app_db.v_users_public AS
SELECT id, name, email, last_login_date
FROM app_db.users;
GRANT SELECT ON app_db.v_users_public TO 'support'@'%';
场景七:账户锁定与密码管理
sql
-- 创建时必须设置密码且过期
CREATE USER 'temp_user'@'%'
IDENTIFIED BY 'Temp@2024'
PASSWORD EXPIRE;
-- 账户锁定(防止未授权使用)
ALTER USER 'temp_user'@'%' ACCOUNT LOCK;
-- 账户解锁
ALTER USER 'temp_user'@'%' ACCOUNT UNLOCK;
-- 强制密码历史(防止重复使用旧密码)
ALTER USER 'temp_user'@'%' PASSWORD HISTORY 5;
-- 限制错误登录尝试(MySQL 8.0.19+)
ALTER USER 'temp_user'@'%'
FAILED_LOGIN_ATTEMPTS 3
PASSWORD_LOCK_TIME UNBOUNDED;
权限分配决策树

最佳实践
使用角色管理权限组
sql
-- 创建标准角色
CREATE ROLE 'app_readonly', 'app_readwrite', 'app_admin';
-- 赋予权限
GRANT SELECT ON app_db.* TO 'app_readonly';
GRANT SELECT, INSERT, UPDATE, DELETE ON app_db.* TO 'app_readwrite';
GRANT ALL ON app_db.* TO 'app_admin';
-- 分配角色给用户
GRANT 'app_readonly' TO 'developer1'@'%';
GRANT 'app_readwrite' TO 'developer2'@'%';
最小权限原则示例
sql
-- 创建只读账号的最佳实践
CREATE USER 'report_user'@'192.168.10.%'
IDENTIFIED BY 'StrongP@ssw0rd!'
PASSWORD EXPIRE INTERVAL 180 DAY;
-- 只授予必要权限
GRANT SELECT ON reporting_db.* TO 'report_user'@'192.168.10.%';
-- 不授予任何其他权限
-- 没有 INSERT/UPDATE/DELETE/ALTER/CREATE/DROP
定期权限审计查询
sql
-- 查看所有用户及其角色
SELECT user, host, account_locked, password_expired
FROM mysql.user;
-- 查看特定用户的权限
SHOW GRANTS FOR 'username'@'host';
-- 查看所有角色
SELECT * FROM mysql.roles_mapping;
-- 查看哪些用户拥有特定数据库的权限
SELECT * FROM mysql.db WHERE db = 'sales_db';
常见问题排查
| 问题 | 可能原因 | 解决方案 |
|---|---|---|
| 授权后无权限 | 缓存未刷新 | FLUSH PRIVILEGES |
| 角色未生效 | 角色未激活 | SET DEFAULT ROLE |
| 密码认证失败 | 8.0 默认 caching_sha2_password |
客户端更新驱动或使用 mysql_native_password |
GRANT 报错 |
8.0 不允许 GRANT 创建用户 |
先用 CREATE USER |
| 账号自动锁定 | 超过 FAILED_LOGIN_ATTEMPTS |
ALTER USER ... ACCOUNT UNLOCK |