先查mysql.user表确认用户'xxx'@'yyy'是否存在且host匹配,再用select user(), current_user()核验实际认证身份,最后执行show grants for current_user()查看真实生效权限。

查 mysql.user 表确认用户是否存在且 host 匹配
错误 Access denied for user 'xxx'@'yyy' 中的 'xxx'@'yyy' 是关键线索,它表示 MySQL 正在查找一个用户名为 xxx、允许从主机 yyy 连接的账户。如果该组合在 mysql.user 表中不存在,或者 host 字段不匹配(比如填了 localhost 却用 127.0.0.1 连),就会直接拒绝。
登录 MySQL(用有权限的账号,如 root)后执行:
SELECT user, host, authentication_string FROM mysql.user WHERE user = 'xxx';
注意检查结果中的 host 值是否与你连接时实际来源一致——常见坑点包括:
-
localhost和127.0.0.1在 MySQL 中是两个不同 host,不能互换 - 云数据库(如阿里云 RDS、AWS RDS)通常禁用
localhost,只允许%或具体 IP -
%表示任意主机,但不包含localhost(MySQL 5.7+ 默认策略) - 若用 Docker 或容器网络,宿主机连容器 MySQL 时,
host往往是容器 IP 或%,不是localhost
授予权限时必须显式指定 @'host',不能省略
执行 GRANT 语句时,'user'@'host' 是完整身份标识,漏掉 @'host' 或写错 host 会导致权限没加到目标账户上。
例如,想让 appuser 从任意地址访问 mydb:
GRANT SELECT, INSERT ON mydb.* TO 'appuser'@'%';
而不是:
GRANT SELECT, INSERT ON mydb.* TO 'appuser'; ← 错!这会创建 'appuser'@'%' 吗?不一定,取决于 SQL_MODE 和版本
常见错误操作:
- 用
CREATE USER 'appuser'@'localhost'创建用户,却用GRANT ... TO 'appuser'(没带 host),结果权限加到了'appuser'@'%'或报错 - 误以为
FLUSH PRIVILEGES能“修复”权限缺失——它只重载权限表,不代替GRANT - MySQL 8.0+ 默认认证插件是
caching_sha2_password,若用老客户端或旧驱动,需显式指定:ALTER USER 'appuser'@'%' IDENTIFIED WITH mysql_native_password BY 'pwd';
检查 authentication_string 是否为空或格式异常
MySQL 5.7+ 把密码存在 authentication_string 字段,而非旧版的 password 字段。如果该字段为空(NULL 或空字符串),即使用户存在、host 匹配,也会报 Access denied(尤其 using password: NO 时)。
使用tbot机器ID身份文件配合tsh CLI,通过Teleport访问控制SSH登录托管主机或执行远程命令。
查当前用户的认证状态:
SELECT user, host, authentication_string, plugin FROM mysql.user WHERE user = 'xxx';
典型问题:
-
authentication_string为NULL:说明该用户没设密码,但你连接时用了-p参数 → 改成不输密码,或设密码 -
plugin是auth_socket(常见于 Ubuntu 自带 MySQL):它跳过密码验证,只认系统用户,此时必须用sudo mysql -u xxx,不能远程连 - 密码字段被手动 UPDATE 过但没用
PASSWORD()或SHA2()加密 → 密码无效,应改用SET PASSWORD或ALTER USER
远程连接失败时,bind-address 和防火墙常被忽略
即使用户权限全开、密码正确,MySQL 服务本身若没监听外部地址,连接请求根本到不了权限校验环节。
检查配置文件(/etc/mysql/mysql.conf.d/mysqld.cnf 或 /etc/my.cnf)中:
bind-address = 127.0.0.1 ← 默认值,只响应本地连接
改为:
bind-address = 0.0.0.0 ← 监听所有接口(生产环境建议限定为内网IP)
改完必须重启服务:sudo systemctl restart mysql;否则配置不生效。
另外,别忘了防火墙:
- 本地
ufw或firewalld是否放行3306端口 - 云服务器(如阿里云 ECS)的安全组规则是否允许目标 IP 访问
3306 - 某些云数据库(RDS)默认关闭公网地址,需单独申请或绑定弹性 IP
真正卡住排查的,往往不是 SQL 权限语句写错,而是 bind-address 没改、防火墙没开、或 host 写成了 localhost 却从 Docker 宿主机连——这三个点反复验证一次,能绕过 70% 的 Access denied 场景。










