必须显式撤销写权限,不能仅靠GRANT SELECT;MySQL权限叠加,残留的INSERT、UPDATE等需逐项REVOKE,SUPER等全局权限须单独处理,且需验证CURRENT_USER()与SHOW GRANTS确保生效。

只读用户必须显式撤销写权限,不能只靠GRANT SELECT
GRANT SELECT ON `mydb`.* 只是“加权限”,不是“清权限”。MySQL 权限是叠加的,如果用户之前被授过 ALL PRIVILEGES 或继承了角色里的 INSERT,那 GRANT SELECT 完全无效。你看到的“能查但也能删”,大概率是因为残留权限没清理干净。
实操建议:
- 先执行
SHOW GRANTS FOR 'username'@'host';,检查输出里是否含INSERT、UPDATE、DROP、ALTER、LOCK TABLES、EXECUTE、GRANT OPTION或SUPER - 确认有就立刻
REVOKE:比如REVOKE INSERT, UPDATE, DELETE ON `mydb`.* FROM 'username'@'host'; - 特别注意
SUPER:它能让用户绕过几乎所有权限检查,必须单独查SELECT Super_priv FROM mysql.user WHERE User = 'username';,是 Y 就得REVOKE SUPER ON *.* FROM 'username'@'host';
MySQL 8.0 创建用户必须分两步:CREATE USER + GRANT
5.7 可以用 GRANT ... IDENTIFIED BY 一步建用户,但 8.0+ 已废弃该语法。直接写会报错:ERROR 1064 (42000): You have an error in your SQL syntax。
实操建议:
- 先创建用户,显式指定认证插件(尤其要兼容旧客户端):
CREATE USER 'ro_user'@'10.20.%' IDENTIFIED WITH mysql_native_password BY 'P@ssw0rd2026'; - 再授权:
GRANT SELECT ON `mydb`.* TO 'ro_user'@'10.20.%'; - 不要用
GRANT SELECT ON *.*:这会把mysql、information_schema等系统库也放开,存在信息泄露风险 - 库名含短横或关键字(如
order_db)必须用反引号包裹,否则语法报错
验证时别复用旧连接,用 CURRENT_USER() 看真实匹配账号
刷新权限后仍连不上、或权限不生效,90% 是 host 匹配问题。MySQL 把 'user'@'localhost' 和 'user'@'127.0.0.1' 当作两个完全不同的账号。
实操建议:
- 连接后立刻执行
SELECT CURRENT_USER();,看实际登录的是哪个账号(比如返回'ro_user'@'192.168.5.100',但你只给'ro_user'@'10.20.%'授权,那就失败) - 再执行
SHOW GRANTS FOR CURRENT_USER;,确认当前会话看到的权限和你预期一致 - 密码改完后别用
UPDATE mysql.user直接改哈希值——8.0+ 密码格式变了,必须用ALTER USER ... IDENTIFIED BY -
FLUSH PRIVILEGES;在 8.0+ 中非必需,CREATE USER和GRANT本身已立即生效;乱刷反而可能掩盖配置错误
需要跨库只读或字段级控制?用角色或视图封装
MySQL 不支持列级 SELECT 权限(比如只让查 users.name,不给 users.phone),也没有 GRANT SELECT(name,email) ON users 这种语法。
实操建议:
- 跨多个业务库只读:逐个
GRANT SELECT ON `sales_db`.*、GRANT SELECT ON `report_db`.*,别偷懒用通配符 - 要隐藏敏感字段:建视图
CREATE VIEW user_basic AS SELECT id, name, email FROM users;,然后GRANT SELECT ON `mydb`.`user_basic` TO 'ro_user'@'host'; - 想统一管理权限组:MySQL 8.0 支持角色,可
CREATE ROLE 'app_reader'; GRANT SELECT ON `mydb`.* TO 'app_reader'; GRANT 'app_reader' TO 'ro_user'@'host'; - 若需
SHOW CREATE TABLE或EXPLAIN,额外补GRANT SELECT ON `information_schema`.`TABLES`和PROCESS权限(谨慎开放)
真正容易被忽略的点是:权限生效不等于语句可执行。比如 SELECT ... FOR UPDATE 会失败,不是因为你漏授了什么权限,而是它隐式依赖 LOCK TABLES——而你在 REVOKE 阶段已经把它干掉了。


















