mysql新建用户权限为空,需显式授权;严格遵循“未授予即拒绝”;操作后须flush privileges刷新,授权与撤销须精确匹配user@host,慎用grant option和all privileges。

MySQL新建用户时权限默认是空的,不是“没权限”,而是“无任何显式授权”
刚创建的用户连 SELECT 都不能执行,哪怕只是查 information_schema。MySQL 不像 PostgreSQL 默认给 public 角色基础权限,它严格遵循“未授予即拒绝”——这是最小权限落地的前提。
实操建议:
- 用
CREATE USER 'u1'@'localhost' IDENTIFIED BY 'pwd';创建后,立刻用SHOW GRANTS FOR 'u1'@'localhost';确认返回空结果,别凭经验假设“应该能连上” - 避免用
GRANT ALL PRIVILEGES ON *.* TO ...初始化用户,哪怕临时测试也不行;ALL PRIVILEGES包含GRANT OPTION和PROXY这类高危权限 - 如果用户只需读某库,直接
GRANT SELECT ON mydb.* TO 'u1'@'localhost';,不要先GRANT USAGE再叠加——USAGE是空权限占位符,无实际作用,反而误导
Revoke 不能撤销隐式继承的权限,只能撤你亲手 GRANT 过的那条
常见错误:用户 A 有 SELECT 权限,你对用户 B 执行了 GRANT SELECT ON mydb.* TO 'B'@'%';,之后又 REVOKE SELECT ON mydb.* FROM 'A'@'localhost';,结果发现 B 的权限还在——因为 REVOKE 只影响目标用户,不波及他人。
更隐蔽的坑是权限层级继承失效:比如你给 'u1'@'%' 授了 SELECT 在 mydb.t1,又给 'u1'@'localhost' 授了 SELECT 在 mydb.*,那么 REVOKE SELECT ON mydb.t1 FROM 'u1'@'%' 不会影响其对 mydb.t2 的访问,但也不会自动降级为只对 t2 有效——权限按 host/user 组合独立存储,互不覆盖。
实操建议:
-
REVOKE前必须确认 target user@host 和当初GRANT时完全一致,大小写、host 通配符(%vslocalhost)都要对得上 - 用
SHOW GRANTS FOR 'u1'@'localhost';查完再 revoke,别靠记忆操作 - 撤销数据库级权限(如
ON mydb.*)不会自动清除该库下已存在的表级权限,要分别REVOKE或用DROP USER彻底清理
GRANT OPTION 不等于“能给别人授权”,它还允许被授者把权限再转授给第三方
开了 GRANT OPTION 的用户,不仅能自己 GRANT,还能在 GRANT 语句末尾加 WITH GRANT OPTION 把授权能力继续传下去。这会导致权限链失控,尤其在团队协作或外包接入场景里极易埋雷。
实操建议:
- 除非明确需要二级授权人(例如 DBA 下放给小组长),否则一律不加
WITH GRANT OPTION - 检查现有用户是否意外持有该权限:
SELECT User,Host,Grant_priv FROM mysql.user WHERE Grant_priv='Y'; - 若已误开,直接
REVOKE GRANT OPTION ON *.* FROM 'u1'@'localhost';即可收回,无需先REVOKE其他权限
权限变更后不生效?不是缓存问题,是没刷权限表
执行完 GRANT 或 REVOKE 后立刻连接测试失败,大概率是因为 MySQL 没重载权限缓存。它不会实时同步到内存,必须显式刷新。
实操建议:
- 执行完权限操作后,**必须**运行
FLUSH PRIVILEGES;,否则新权限不会加载 - 注意:只有通过直接修改
mysql.user等系统表改权限才强制需要FLUSH;用GRANT/REVOKE语句操作时,多数版本(5.7+)会自动触发刷新,但保险起见仍建议加上 - 如果用的是 MySQL 8.0+,且启用了角色(
CREATE ROLE),则需额外SET DEFAULT ROLE或GRANT ROLE才能让角色生效,光FLUSH PRIVILEGES不够
权限粒度比想象中细,host 匹配顺序、大小写敏感性、通配符范围都可能让一条 GRANT 实际生效范围偏移。别依赖“差不多能用”,每次授权后用目标用户连接,跑一句 SELECT USER(), CURRENT_USER(); 看看到底是以哪个身份鉴权。











