拓冰建站拓冰建站
首页 / 资讯中心 / 正文

MySQL ERROR 1045 (28000) Access denied for root@localhost 排查与解决

在Linux和Windows服务器上折腾MySQL的朋友几乎没有人能绕开这个报错ERROR 1045 (28000): Access denied for user rootlocalhost (using password: NO)。尤其当你明明记得密码、或者刚装完MySQL第一次登录、甚至只是换了一台机器连接远程数据库它都会突然蹦出来拦你一下。这行报错翻译成人话就是MySQL拒绝了从本机发起的root用户连接请求而且服务端认为你压根没带密码来。但这里有个特别误导人的地方——错误里写的NO不代表你输入的密码是错的而是MySQL在告诉你“我收到的握手包里没有密码字段”。很多时候你明明在命令行敲了密码报错却依然显示NO原因往往不是你记错了密码而是客户端根本没把密码传过去。这篇文章我会把这个错误的常见成因拆开揉碎从环境变量、认证插件、密码过期策略、权限表损坏到skip-grant-tables重置密码一层层往下讲最后附上我实际踩坑和排查的记录。不管你是刚装好MySQL的新手还是被这个错卡了半天的老运维这篇内容应该都能帮你省下不少时间。1. 错误含义拆解与快速定位1.1 报错信息里的三个关键信息先看这行报错的标准格式ERROR 1045 (28000): Access denied for user rootlocalhost (using password: NO)拆解一下三个核心信息ERROR 1045 (28000)这是MySQL的错误码和SQLSTATE码。28000在SQL标准里属于“客户端认证失败”这一类意思很直白——连接被拒绝了。rootlocalhost说的是你试图以root用户身份从本机(localhost)连入MySQL。注意这里localhost并不是指你的操作系统主机名而是MySQL权限表mysql.user里Host字段的值。using password: NO表示服务端收到的认证请求里没有携带密码。如果你在命令行带了-p参数这里会显示YES如果显示NO说明密码确实没传过去。很多人在这一步就陷入了误区以为自己密码错了就开始一遍遍重输。实际上如果是密码错误报错通常会显示using password: YES显示NO恰恰说明问题出在“密码没传过去”或者“root账户本身没有设置密码”。1.2 常见触发场景有哪些根据我接触过的案例这个错误最常出现在以下几个场景刚装完MySQL或MariaDB第一次用mysql -u root登录但安装过程里设置的root密码和你输入的不一致。在脚本或程序里配置了数据库连接但连接串里没写密码或密码字段被空字符串覆盖。使用了mysql命令但没加-p参数MySQL客户端在非交互模式下不会弹窗提示输入密码直接就以无密码方式尝试连接。修改过root密码后没有执行FLUSH PRIVILEGES导致权限表里缓存的认证信息还是旧的。MySQL的root账户配置了auth_socket或unix_socket插件这种插件只认操作系统用户身份不认密码也会出现类似报错。我在排查问题的时候第一步永远都是先区分是“没传密码”还是“密码不对”这两者的解决方向完全不同。1.3 快速判断问题方向的三个命令拿到这个报错后不用急着改配置先执行下面三个命令快速判断# 查看MySQL服务是否正常运行 systemctl status mysql # 或 service mysql status # 带密码尝试连接观察报错变化 mysql -u root -p # 查看MySQL版本和默认认证插件 mysql --version如果服务是正常运行的执行mysql -u root -p之后等密码输入框出现时随便输一个密码观察报错是继续保持NO还是变成YES。如果变成YES说明问题出在密码认证本身如果依然是NO说明你的MySQL客户端可能有问题比如my.cnf里被配置了[client]段的默认参数。2. 逐个排查为什么密码没传过去2.1 环境变量和配置文件里的坑在这里我复盘一个真实案例。一个朋友联系我说MySQL连不上了报错就是1045 using password: NO。我远程看了一下发现他执行的是mysql -u root没有加-p参数。我说你直接这样MySQL当然不会弹密码框它就默认用空密码去连。他回答说以为MySQL装了之后root就是空密码直接回车就能进。这就是最典型的新手误区。在很多Linux发行版里装完MySQL后root默认使用auth_socket插件认证密码是无效的你需要用sudo mysql才能进入。而在另一些发行版里root确实有初始随机密码存放在/var/log/mysqld.log或者/etc/mysql/debian.cnf里。这两种情况都不会直接让你用一个自定义密码顺利登录。还有一类情况是my.cnf里配置了[client]段落里面写了userroot但没有写password或者写了password但值为空。这样即使你在命令行加了-p参数配置文件里的空密码也可能覆盖命令行输入导致服务端收到的依然是空密码。检查一下你的配置文件# 查看MySQL配置文件中是否有[client]段的默认配置 cat /etc/mysql/my.cnf cat /etc/my.cnf ls -la /etc/mysql/conf.d/如果发现[client]段里有password字段先把它注释掉再试。因为MySQL客户端读取配置文件的优先级是命令行参数 环境变量 配置文件但配置文件里的password字段如果存在在某些版本里反而会优先于命令行输入被发送出去。2.2 环境变量MYSQL_PWD的隐秘影响有一种很隐蔽的情况是环境变量里设置了MYSQL_PWD。我记得有次帮人排查一个生产环境的MySQL连接问题代码里明明写了密码数据库端日志却始终显示using password: NO。查了半天才发现是这个开发者在bashrc里导出过MYSQL_PWD后来虽然改回了正确密码但环境变量没删应用程序启动时继承了这个旧的环境变量导致连接时密码被覆盖成空字符串。MYSQL_PWD这个环境变量的优先级非常高如果你的脚本里同时使用了mysql命令并依赖它读取密码而环境变量又是一个空字符串那MySQL客户端就会发送一个空的密码字段服务端自然显示using password: NO。排查方法# 检查当前环境变量 echo $MYSQL_PWD # 在所有常见的shell配置文件中搜索 grep -r MYSQL_PWD ~/.bashrc ~/.bash_profile ~/.profile /etc/profile 2/dev/null如果确认是这个环境变量导致的直接unset MYSQL_PWD或者从配置文件里删掉相关行。2.3 客户端与服务端版本兼容性MySQL 8.0默认使用caching_sha2_password认证插件而老版本的客户端如5.6、5.7默认使用mysql_native_password。当你用老版本客户端连接MySQL 8.0服务端时认证流程会变得很复杂可能需要额外的SSL或RSA公钥交换如果客户端不支持就会出现认证被打断的报错。虽然大部分情况下这会表现为using password: YES但在某些特定组合下也可能出现密码根本没有成功发送的异常行为。版本兼容性排查相对简单先确认两边的版本-- 在能登录的情况下查看 SELECT VERSION(); SHOW VARIABLES LIKE default_authentication_plugin;如果服务端是8.0客户端是5.x建议升级客户端到8.x系列或者将用户的认证插件改成mysql_native_password这个我们后面会详细说。3. 核心解决办法从临时登录到彻底修复3.1 最稳妥的初始登录方式sudo mysql如果你是在Linux系统上新装的MySQLroot用户大概率使用了auth_socket认证插件。此时不需要密码只需要以系统root用户身份执行mysql命令即可sudo mysql成功进入后可以查看当前用户的认证方式SELECT user, host, plugin FROM mysql.user WHERE user root;如果plugin显示为auth_socket那就可以确定root用户走的是Unix套接字认证。这种方式的好处是很安全因为只有系统root用户能登录坏处是你在程序中无法用root用户连接数据库。如果你希望root也能用密码登录可以执行ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 你的新密码; FLUSH PRIVILEGES;执行完后退出再用mysql -u root -p登录。这里要注意的是MySQL 8.0里默认认证插件是caching_sha2_password如果你希望用mysql_native_password需要确认服务端是否还支持这个插件。如果不支持可以直接用默认插件ALTER USER rootlocalhost IDENTIFIED BY 你的新密码;这条命令在MySQL 8.0里会自动使用default_authentication_plugin指定的插件来设置密码。3.2 跳过权限表重置root密码如果你的MySQL已经无法登录连sudo mysql都进不去比如auth_socket插件被改坏了或者root密码确实忘记了那就需要走跳过权限表这一步。核心思路是让MySQL启动时不加载权限表这样你就能无密码进入系统然后手动重置密码。具体步骤如下第一步停止MySQL服务sudo systemctl stop mysql第二步以跳过授权表的方式后台启动MySQLsudo mysqld_safe --skip-grant-tables --skip-networking 这里--skip-networking很关键它会让MySQL只监听本地套接字不监听网络端口避免在无认证状态下被外部连接攻击。第三步直接无密码登录sudo mysql -u root此时不需要密码就能进入因为权限表没加载。第四步刷新权限表并重置密码FLUSH PRIVILEGES; ALTER USER rootlocalhost IDENTIFIED BY 新密码; FLUSH PRIVILEGES;如果执行ALTER USER时报错提示root用户不存在或plugin不可用可以先执行UPDATE mysql.user SET authentication_string WHERE user root; FLUSH PRIVILEGES;然后再执行ALTER USER。第五步退出并重启MySQL服务exit sudo pkill mysqld_safe sudo systemctl start mysql之后就可以用新密码正常登录了。3.3 修改root密码的完整注意事项重置密码这件事看起来简单但有几个细节处理不好会把你坑在原地。第一个坑是skip-grant-tables模式下密码修改后忘了清理mysqld_safe进程。如果你直接service mysql start可能因为端口被占用或pid文件冲突而启动失败表现就是MySQL一直起不来或者起来后你怎么也连不上。解决办法是先用ps aux | grep mysql确认所有mysqld进程都退出了再启动服务。第二个坑是密码策略问题。MySQL 8.0默认的密码策略是validate_password组件要求密码长度至少8位并且包含大小写字母、数字和特殊字符。如果你设置的新密码太简单ALTER USER会直接报错。遇到这种情况要么设置一个复杂度足够的密码要么暂时卸载这个组件UNINSTALL COMPONENT file://component_validate_password;重置完密码后再装回来INSTALL COMPONENT file://component_validate_password;第三个坑是mysql.user表中host字段的匹配规则。rootlocalhost和root%是两个完全不同的账户。你改了localhost的密码不代表%的密码也变了。如果你之后是通过远程IP连接实际命中的是root%账户密码还是旧的照样会报Access denied。3.4 图形化工具连接MySQL 8.0时的1045问题很多人遇到的1045其实发生在图形化工具上比如Navicat、DataGrip、DBeaver。这类工具的报错通常是Access denied for user rootlocalhost (using password: YES)。但也有一些老版本工具在连接MySQL 8.0时会先发送一个空密码的探测请求某些情况下会直接显示NO。根本原因是MySQL 8.0默认的caching_sha2_password认证插件需要客户端支持。老版本的Navicat12.1.20以下不支持这个插件所以要么升级工具要么把用户的认证插件改回mysql_native_passwordALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 你的密码; FLUSH PRIVILEGES;在MySQL 8.4版本里mysql_native_password插件默认被禁用了所以用这种方式之前需要先确认服务端是否加载了该插件SHOW PLUGINS;如果看到mysql_native_password的状态是DISABLED就需要在my.cnf里配置启用[mysqld] mysql_native_passwordON修改后重启MySQL服务。不过我要提醒一下长期来看还是建议升级客户端工具而不是把服务端的安全标准降级毕竟mysql_native_password相比caching_sha2_password来说安全性弱了不少。4. 实操过程复盘从报错到修复的完整排查记录4.1 案例背景与初始状态我这里可以分享一次完整的排查案例。环境如下操作系统Ubuntu 22.04 LTS数据库MySQL 8.0.32场景新装MySQL后执行mysql -u root报错ERROR 1045 (28000): Access denied for user rootlocalhost (using password: NO)安装方式apt install mysql-server在Ubuntu的apt源里安装MySQL 8.0不会让你设置root密码而是自动使用auth_socket插件。这意味着你无论输什么密码都是错的而且因为没输入密码报错就是NO。4.2 排查过程与每一步的判断逻辑第一步先确认服务状态systemctl status mysql输出显示active (running)服务正常。第二步用sudo mysql尝试登录sudo mysql成功进入。查看root用户的认证方式SELECT user, host, plugin FROM mysql.user WHERE userroot; ---------------------------------------- | user | host | plugin | ---------------------------------------- | root | localhost | auth_socket | ----------------------------------------确认了就是auth_socket插件在生效。第三步修改认证插件和密码ALTER USER rootlocalhost IDENTIFIED WITH caching_sha2_password BY MyNewPass123!; FLUSH PRIVILEGES;这里我特意指明用caching_sha2_password是因为MySQL 8.0默认就支持它而且安全性更高。如果你用Navicat这类工具可以改成mysql_native_password我这里为了验证新版客户端的兼容性就保持了默认插件。第四步退出后正常登录exit mysql -u root -p输入密码后成功进入报错消失。4.3 过程中遇到的一个小插曲在第三步执行ALTER USER时第一次我设置了一个比较简单的密码比如root123结果报错ERROR 1819 (HY000): Your password does not satisfy the current policy requirements这是validate_password组件在起作用。解决办法有两种要么设置复杂密码要么临时卸载组件。我当时直接改用了强密码MyNewPass123!符合大小写数字特殊字符的要求就顺利通过了。如果你确实想设置简单密码可以这样操作-- 查看当前密码策略 SHOW VARIABLES LIKE validate_password%; -- 临时降低策略等级 SET GLOBAL validate_password.policy LOW; SET GLOBAL validate_password.length 4;不过这只建议在本地测试环境使用生产环境一定要用强密码。4.4 本地可以登录了远程还是报错怎么办这个问题经常跟在1045后面出现。本地root能登录了但用Navicat连接服务器IP时还是报Access denied。原因往往是命中账户不同。MySQL的账户匹配规则是精确优先、模糊靠后。root账户可能有多个SELECT user, host, plugin FROM mysql.user WHERE user root;常见的有userhostpluginrootlocalhostcaching_sha2_passwordroot%caching_sha2_passwordroot127.0.0.1caching_sha2_password如果你只改了rootlocalhost的密码用IP远程连接时会命中root%或root127.0.0.1账户密码当然不对。处理办法-- 修改root在所有host下的密码 ALTER USER root% IDENTIFIED BY 新密码; -- 如果%不存在需要先创建 CREATE USER root% IDENTIFIED BY 新密码; GRANT ALL PRIVILEGES ON *.* TO root% WITH GRANT OPTION; FLUSH PRIVILEGES;远程连接还需要确认bind-address配置如果MySQL只监听了127.0.0.1外部IP是连不进来的# 查看监听地址 ss -tlnp | grep 3306如果显示127.0.0.1:3306说明只监听本地需要在my.cnf的[mysqld]段设置bind-address 0.0.0.0然后重启MySQL。这一条只针对远程连接场景本地连接不受影响。5. 认证插件问题MySQL 8.x的隐藏雷区5.1 三种认证插件对比MySQL 8.0带来的最大变化之一就是默认认证插件从mysql_native_password换成了caching_sha2_password。很多老项目升级数据库版本后突然连不上问题就出在这里。插件名密码哈希算法安全强度客户端兼容性mysql_native_passwordSHA1低所有版本通用caching_sha2_passwordSHA256高MySQL 8.0驱动才支持sha256_passwordSHA256高需要SSL或RSA如果你用的是老版本的PHP mysqli扩展、老版本Navicat或者一些极简的JDBC驱动默认不认识caching_sha2_password。连接时可能报1045认证失败也可能报Unable to load authentication plugin caching_sha2_password。5.2 如何将用户改为mysql_native_password在MySQL 8.0里切换用户认证插件的方式ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 你的密码; FLUSH PRIVILEGES;在MySQL 8.4及以后版本mysql_native_password插件默认被禁用。如果你确认需要这个插件可以在my.cnf里显式启用[mysqld] mysql_native_passwordON重启MySQL后再执行上述ALTER USER命令。不过我建议你尽量反向操作升级客户端驱动。比如Navicat升级到16以上版本PHP使用mysqlnd驱动JDBC使用mysql-connector-java 8.0以上版本。这些版本都能正常支持caching_sha2_password。5.3 开发环境快速切换的临时方案如果你只是本地开发环境图省事可以把default_authentication_plugin直接改成mysql_native_password。在my.cnf的[mysqld]段[mysqld] default_authentication_plugin mysql_native_password在MySQL 8.4里这个变量被移除了但你仍然可以用上面的mysql_native_passwordON方式。改完后重启MySQL新建的用户就都会默认用mysql_native_password插件。注意这只影响新建用户老用户需要手动ALTER USER。我个人的建议是能用新插件就用新插件别因为图省事把安全标准往下降。生产环境尤其不要动默认认证插件。6. 常见问题与排查技巧实录6.1 问题速查表场景报错信息特点可能原因推荐解法新装MySQL首次登录using password: NOroot使用auth_socket插件或初始随机密码sudo mysql进入后ALTER USER密码输对了还是被拒using password: YES密码哈希不匹配、账号被锁定重置密码或用skip-grant-tables远程连接被拒using password: YES命中错误host账户、bind-address限制检查mysql.user匹配规则老工具连MySQL 8using password: YEScaching_sha2_password插件不支持升级工具或改mysql_native_password代码连接数据库被拒using password: NO环境变量MYSQL_PWD为空unset MYSQL_PWD密码过期using password: YESdefault_password_lifetime策略ALTER USER修改密码账户被锁Access denied且日志有Locked多次登录失败触发锁定ALTER USER ... ACCOUNT UNLOCK6.2 一个容易忽略的坑密码双引号问题有一种情况密码里包含了特殊字符比如分号、井号、空格在命令行里直接传参时会被Shell拦截mysql -u root -pMy#Pass如果密码里有#在Shell里#后面会被当成注释实际传过去的密码是My自然就报认证失败。正确做法是用单引号包住密码mysql -u root -pMy#Pass或者只输入-p让MySQL自己提示输入密码这样最安全因为密码不会出现在进程列表里。6.3 日志排查法看错误日志比瞎猜快一百倍如果上面所有办法都试过了还是不行那就别在命令行里猜了直接看MySQL的错误日志# Ubuntu/Debian sudo tail -100 /var/log/mysql/error.log # CentOS/RHEL sudo tail -100 /var/log/mysqld.log错误日志里会详细记录认证失败的具体原因是密码错误、账户不存在、还是认证插件不匹配。我遇到过有人修改了系统hostname导致mysql.user表里root的host字段从localhost变成了新主机名然后怎么连都报1045。这种问题只看命令行报错根本发现不了一翻日志就一目了然。6.4 关于FLUSH PRIVILEGES的使用心得很多人一遇到权限问题就执行FLUSH PRIVILEGES但实际上这个命令在大多数情况下是多余的。当你使用ALTER USER、CREATE USER、GRANT等SQL语句修改权限时MySQL会自动重新加载权限表不需要手动刷新。只有在以下两种情况才需要FLUSH PRIVILEGES直接操作了mysql.user表比如UPDATE语句修改密码使用INSERT或DELETE直接增删了权限记录除此之外频繁执行FLUSH PRIVILEGES反而会造成不必要的锁开销。所以重置密码后如果用的是ALTER USER不用执行FLUSH PRIVILEGES如果是用UPDATE改的mysql.user表那就要执行。6.5 密码过期策略导致的坑MySQL 8.0有一个默认的密码过期策略可以用default_password_lifetime参数控制默认值是0表示不过期。但有些安全加固脚本会把它设置成180或90天导致root密码到期后无法登录表现也是Access denied。排查方法-- 查看root密码是否过期 SELECT user, host, password_expired FROM mysql.user;如果password_expired是Y即使密码输对了也会被拒绝需要重新设置密码ALTER USER rootlocalhost IDENTIFIED BY 新密码;或者把密码过期时间改成永久ALTER USER rootlocalhost PASSWORD EXPIRE NEVER;这个问题在测试环境里特别容易和忘了密码混淆排查时多看一眼这个字段能省不少事。7. 最后的经验与建议在我实际处理过的1045报错案例里至少有一半以上不是密码本身的问题而是认证方式不匹配、host匹配错误、或者配置文件参数覆盖。所以拿到这个报错先别急着重置密码按照这篇文章的排查顺序走一遍基本上都能快速定位到根因。还有一个特别实用的小习惯我会在每台服务器的MySQL配置文件里加上一行注释记录当前root账号的认证方式和密码策略省得下次维护时还要现查。用完skip-grant-tables模式以后一定要确认mysqld_safe进程已经被杀掉再启动正常的MySQL服务不然端口冲突会让你误判成服务起不来。另外如果是在容器环境Docker里遇到这个报错情况还会多一层——你进去用的可能是匿名卷或者挂载的数据目录密码保存在旧的数据卷里和容器默认配置对不上。这种情况直接docker exec进入容器查看mysql.user表能更快定位问题。希望这篇文章能帮你把ERROR 1045这个拦路虎彻底解决掉。如果你在排查过程中遇到文章里没覆盖到的情况欢迎留言补充我看到了会尽量回复。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门