MySQL 中如何授予用户只读权限
只读权限意味着用户只能执行
SELECT,不能修改数据或结构。最稳妥的做法是显式授予
SELECT权限,且不授予其他 DML 或 DDL 权限。
常见错误是误用
USAGE(它实际表示“无任何权限”,不是只读)或遗漏
GRANT OPTION的禁用(否则用户可能自行授权他人)。 对单个数据库授只读:使用
GRANT SELECT ON `db_name`.* TO 'user'@'host';若需跨库只读,需为每个库单独执行
GRANT SELECT;MySQL 不支持通配多个库名 执行后必须运行
FLUSH PRIVILEGES;(仅在直接操作
mysql系统表后才强制需要;通过
GRANT语句赋权通常自动生效,但部分旧版本或特殊部署建议刷新) 注意:视图、存储过程等对象的访问还取决于其定义者权限和
SQL SECURITY设置,
SELECT权限本身不自动覆盖这些
MySQL 中如何授予用户读写权限
读写权限一般指允许
SELECT、
INSERT、
UPDATE、
DELETE,但不含结构变更(如
CREATE、
DROP)。这是应用账号最常见的最小权限模型。
不要直接授予
ALL PRIVILEGES—— 它包含
GRANT OPTION、
ALTER ROUTINE、
FILE等高危权限,极易引发越权或安全风险。 标准读写(不含建表/删表):
GRANT SELECT, INSERT, UPDATE, DELETE ON `db_name`.* TO 'user'@'host';如果应用需要清空表(
TRUNCATE),注意:MySQL 中
TRUNCATE需要
DROP权限(因其本质是删表重建),不是
DELETE权限能覆盖的 若业务涉及临时表(如子查询物化),还需额外授予
CREATE TEMPORARY TABLES权限 权限作用域尽量精确到库(
`db_name`.*),避免用
*.*;生产环境禁止对
mysql系统库授任何非 DBA 用户权限
撤销权限与检查当前权限
权限变更后,务必验证是否生效。MySQL 不会自动回收已存在的活跃连接的权限缓存,旧连接仍持有赋权前的权限快照,需重新登录或等待连接复用失效。
撤销某类权限:REVOKE INSERT, UPDATE ON `db_name`.* FROM 'user'@'host';(注意:不是
DROP,而是
REVOKE) 查看用户所有权限:
SHOW GRANTS FOR 'user'@'host';查看当前会话权限:
SHOW PRIVILEGES;不显示当前用户权限,应改用
SELECT * FROM INFORMATION_SCHEMA.ROLE_TABLE_GRANTS;(8.0+)或更直接的方式:
SELECT CURRENT_USER(), USER();+
SHOW GRANTS;权限继承关系复杂:角色(8.0+)可简化管理,但若用角色,需确认用户已
SET DEFAULT ROLE,否则角色权限不会激活
常见权限相关错误与绕过风险
权限配置看似简单,但几个关键点容易被忽略,导致“以为限制了,其实没生效”:
localhost和
127.0.0.1在 MySQL 中被视为不同 host —— 用户
'user'@'localhost'和
'user'@'127.0.0.1'是两个独立账户,权限需分别设置 通配符主机名(如
'user'@'%')优先级低于具体 IP,但若存在
'user'@'192.168.1.%'和
'user'@'%',MySQL 按最长匹配原则选择,不是按书写顺序 即使用户只有
SELECT权限,若其能访问
INFORMATION_SCHEMA(默认所有用户可查),就能枚举库表结构;如需进一步收敛,需用
--skip-show-database启动参数或限制
SHOW DATABASES权限(8.0.12+ 支持
SHOW DATABASES权限开关) 函数/存储过程执行权限独立于表权限:即使用户无
SELECT权限,只要拥有
EXECUTE权限且过程定义为
SQL SECURITY DEFINER,就可能间接读取数据 —— 这是典型的权限绕过场景
权限粒度控制在 MySQL 里不是一劳永逸的事,尤其当引入视图、过程、事件或复制拓扑时,权限上下文会动态变化。上线前最好用目标用户账号实际执行一遍典型 SQL 流程,而不是只依赖
SHOW GRANTS输出判断。
