确认用户是否存在且 host 匹配
权限问题八成卡在第一步:用户压根没建对,或者 host 字段不匹配。MySQL 把
'user'@'localhost'和
'user'@'127.0.0.1'当作两个完全不同的账号——前者走 Unix socket,后者走 TCP 连接,哪怕在同一台机器上也会登录失败。 执行
SELECT User, Host FROM mysql.user;,重点看目标用户有没有对应你连接方式的
Host值(远程连就该有
%或具体 IP;本地命令行连
localhost才合理) 如果应用报
Access denied for user 'app'@'192.168.5.23',但
mysql.user里只有
'app'@'%',那没问题;可如果只有
'app'@'192.168.5.%',而客户端 IP 是
192.168.6.10,那就匹配不上 别忽略匿名用户干扰:
DROP USER ''@'localhost';可清理掉空用户名账户,避免它意外“抢”走权限匹配
用 SHOW GRANTS 看真实生效的权限
SHOW GRANTS FOR 'username'@'host';是唯一能反映当前权限状态的权威命令。它不读缓存、不猜逻辑,直接从权限表拼出你实际拥有的权限语句。 如果返回
ERROR 1141 (42000): There is no such grant defined for user,不是权限没生效,是这个账号根本不存在——检查拼写、大小写、单引号是否漏了 注意通配符陷阱:
GRANT SELECT ON *.*授予的是全局权限,但生产环境通常只需
GRANT SELECT ON mydb.*;过度授权可能被中间件或审计策略拦截 权限不是叠加生效,而是按层级“就近匹配”:表级
INSERT权限会覆盖库级
REVOKE INSERT,但列级拒绝规则(存在
mysql.columns_priv表中)不会出现在
SHOW GRANTS输出里,需单独查
排查底层权限表是否被手动修改
有些团队曾直接
UPDATE mysql.user或插入
mysql.tables_priv记录来“快速”调权,结果导致权限行为异常——因为这些操作绕过了 MySQL 的权限校验逻辑,且
SHOW GRANTS不显示它们。 查表级限制:
SELECT * FROM mysql.tables_priv WHERE User='app' AND Host='%' AND Db='sales';,若
Table_priv是空字符串或只含
Select,说明其他操作(如
Insert)被显式禁止 查列级限制:
SELECT * FROM mysql.columns_priv WHERE User='app' AND Column_name='password_hash';,这类细粒度控制常被遗忘,却能导致
SELECT *成功但
SELECT password_hash失败 除非必要,别直改系统表;所有授权尽量用
GRANT/
REVOKE,它们会自动维护一致性
验证网络与认证插件是否拖后腿
能连上不等于权限配置对——连接阶段就失败,往往和权限无关,而是网络或认证机制拦住了。
检查bind-address:如果配置是
127.0.0.1却尝试从远程连,会直接报
Can't connect to MySQL server(错误 2003),不是权限错误(1045) 确认认证插件:
SELECT user, host, plugin FROM mysql.user WHERE user='app';,若插件是
auth_socket或
caching_sha2_password但客户端不支持,就会提示密码错误;可统一改回
mysql_native_password:
ALTER USER 'app'@'%' IDENTIFIED WITH mysql_native_password BY 'xxx';防火墙和安全组必须放行 3306 端口,且只允许可信网段——
iptables -A INPUT -p tcp --dport 3306 -s 10.10.20.0/24 -j ACCEPT比开放
0.0.0.0/0安全得多
真正容易被忽略的,是权限作用域的“精确匹配”特性:MySQL 不会合并不同 host、不同 db、不同 table 的授权记录,也不会把
SELECT权限自动扩展到视图或存储过程。每次怀疑权限不对,先确认你是用哪个
user@host登的、连的是哪个 IP、查的是哪张表——三者缺一不可。
