ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

MySQL 8.0认证协议报错详解:caching_sha2_password兼容与解决方案

MySQL 8.0认证协议报错详解:caching_sha2_password兼容与解决方案 最近接触MySQL 8.0的朋友十个里有八个会撞上这个报错Client does not support authentication protocol requested by server; consider upgrading MySQL client。第一次看到的时候我一度以为是密码输错了反复核对之后发现密码一点问题都没有数据库日志里也看不出异常。后来排查了一圈才确定这是MySQL 8.0把默认认证协议换掉了而你的客户端还停留在旧时代两边握手时鸡同鸭讲自然连不上。今天这篇就围绕这个报错把原理、解决方案、实操命令和踩坑经验一次性讲清楚。先说结论这个问题不是密码问题也不是网络问题而是认证协议不兼容。MySQL 8.0默认的认证插件是caching_sha2_password而老版本MySQL5.x和早期8.0用的都是mysql_native_password。如果你的客户端是旧版Navicat、PHP 5.x/7.0的mysqli扩展、老版JDBC驱动或者干脆是你自己写的Python/PHP连接脚本它们内置的协议列表里没有新插件服务端一抛出caching_sha2_password客户端就懵了报错信息就是这么来的。这篇内容适合所有被这个报错卡住的人不管你是开发、运维还是自己捣鼓服务器的爱好者下面三套方案里总有一个能让你解围。1. 报错的来龙去脉为什么MySQL 8.0要动认证协议1.1 一段报错信息引发的排查先看一下典型的完整报错方便你确认自己是不是同一个问题ERROR 1045 (28000): Access denied for user rootlocalhost (using password: YES)或者是连接工具里的提示Client does not support authentication protocol requested by server; consider upgrading MySQL client前者是密码错误或者权限问题的通用报错后者才是认证协议不兼容的专属提示。我重点聊后者。这种报错有个特点你在服务器命令行里用mysql -uroot -p敲密码能登录但换上Navicat、Workbench的旧版本、或者程序里的连接池就报错。为什么命令行能进因为命令行客户端是新版MySQL自带的它认识新协议而你的图形工具或者代码驱动是老版本它只认识旧的mysql_native_password于是两边握不上手。1.2 认证插件的前世今生MySQL的认证插件机制简单说就是服务器告诉客户端我支持哪些密码校验方式客户端从中挑一个双方都认识的来完成握手。5.7及更早版本默认是mysql_native_password它把密码做一次SHA1散列后传输校验逻辑简单但安全性偏弱而且容易受到碰撞攻击。8.0推倒重来换成了caching_sha2_password。这个名字里带caching是因为它在服务端加了缓存机制认证速度快同时使用SHA256级别的散列安全性高出不少。理论上这是好事但问题在于协议不向后兼容老客户端不认识这个新插件连接时客户端和服务端在协商阶段就会失败而不是到了验证密码才失败。所以哪怕你密码没错也照样被拒。1.3 哪些客户端最容易撞上根据我自己的使用经验下面这些场景几乎百分百踩雷Navicat16.x之前的老版本对MySQL 8.0的caching_sha2_password支持一直不彻底很多用户升级MySQL后被迫跟着升级Navicat。PHP mysqli / PDO mysqliPHP 7.0以下的内置mysqlnd对caching_sha2_password支持不完整连接时经常报错。Java JDBC驱动8.0.20之前的mysql-connector-java版本对新的认证插件支持不理想需要升级驱动或用特殊参数。Python的pymysql、MySQLdb老版本不支持新协议一般升级到新版本即可。部分Python/Node老连接库原理一样版本过旧握手失败。所以这个报错的根源本质上是一个兼容性升级问题数据库升级了客户端没跟上。理解了这一点后面的所有解法就都顺理成章了。2. 三套解决方案与选型逻辑2.1 方案一把用户的认证插件改回旧版最直接的方式是用命令行把对应MySQL账号的认证插件改成mysql_native_password。我一开始也是这么干的因为它见效快改完立刻就能连上。ALTER USER youruserhost IDENTIFIED WITH mysql_native_password BY yourpassword; FLUSH PRIVILEGES;这个方案的核心逻辑是让MySQL迁就客户端。你不需要动任何客户端配置把账号的认证方式降级到老协议客户端就能正常握手了。注意两点第一youruser和host要精确匹配如果root账号实际是通过rootlocalhost连接的你改root%是无效的别问我怎么知道的第二密码会被重新设置改动后记得同步到你的程序配置里。2.2 方案二新建一个专用账号有时候你不想动root这种超级账号怕影响线上环境那就可以新建一个专门给应用或工具使用的账号授权时只开必要的库表权限。这种方式更干净也是我更推荐的日常做法。CREATE USER app_userlocalhost IDENTIFIED WITH mysql_native_password BY StrongPass123; GRANT SELECT, INSERT, UPDATE, DELETE ON mydb.* TO app_userlocalhost; FLUSH PRIVILEGES;好处很明显它把兼容性问题隔离到专用账号上root依旧保持8.0的安全策略如果哪天客户端升级了可以随时把新账号换成caching_sha2_password不影响底层。缺点是如果项目里到处都是连接字符串你得挨个替换。2.3 方案三升级客户端或驱动如果你手里的软件支持升级这是最省心的方向。Navicat升级到16.x以上或换到DBeaver、DataGrip这类活跃更新的工具Java驱动升级到mysql-connector-j 8.0.20PHP 7.4的mysqlnd已经能处理新认证协议。升级客户端的好处是不用改数据库保留MySQL 8.0更强的认证机制。但有些老项目尤其生产服务器上的PHP版本不敢随便动这时候就不得不走方案一或方案二。2.4 怎么选更合理我把三套方案列个表方便你按自己的情况对照方案改动范围安全影响适用场景修改root认证插件数据库账号密码加密强度降级本地开发、测试环境新建专用账号新建账号授权隔离风险可控性强生产环境、多业务隔离升级客户端客户端/驱动数据安全无影响能升级且愿意升级的团队生产环境我倾向方案二因为root账号保持新协议安全底线不后退开发环境图省事用方案一也完全没问题。方案三最理想但受外部条件限制多不是每次都能说升就升。我自己的习惯是先看客户端能不能升级不能就新建专用账号最后才考虑降级已有账号的认证插件。3. 实操过程全记录把命令挨个跑一遍3.1 登录MySQL确认账号当前的认证插件动手之前先登录MySQL服务器执行这条语句看看到底是哪些账号、什么host、什么插件SELECT user, host, plugin, account_locked FROM mysql.user;输出大概长这样---------------------------------------------------------------------- | user | host | plugin | account_locked | ---------------------------------------------------------------------- | root | localhost | caching_sha2_password | N | | root | % | caching_sha2_password | N | | mysql.session | localhost | caching_sha2_password | Y | | mysql.sys | localhost | caching_sha2_password | Y | ----------------------------------------------------------------------看到plugin列全是caching_sha2_password就确认了问题所在。注意host列root和%是两回事你要根据实际连接方式修改对应的那条记录。3.2 修改认证插件的关键命令假设你要把root在localhost下的认证方式改回旧协议执行ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 你想要的密码; FLUSH PRIVILEGES;这里一定要说明为什么用ALTER USER ... IDENTIFIED WITH而不是SET PASSWORD。因为IDENTIFIED WITH plugin BY password能同时指定插件和密码一条命令搞定而SET PASSWORD在某些配置下只会改密码不会动插件类型容易给人留下改了没生效的错觉。执行完再查一次SELECT user, host, plugin FROM mysql.user WHERE user root;看到plugin变成了mysql_native_password基本就成了。3.3 验证连接确认权限和密码都正常改完不要急着关终端立刻开一个新终端用客户端工具或命令行试连。命令行可以这样mysql -uroot -p -h127.0.0.1 -P3306注意我用的是127.0.0.1而不是localhost启动因为有些连接工具走TCP而命令行直接敲mysql -uroot -p走socket两者走的路不一样验证出来的结果可能不同。这一步是为了模拟外部客户端场景确保是真的能通过TCP连上。如果回到Navicat或程序里测试也一并验证。能连上之后再顺手执行一下你常用的查询确认权限没受牵连。因为在改插件的过程中只要你没有误改host或误删账号权限不会丢但总归要实测一下才放心。3.4 密码策略组件带来的额外限制这里有个容易翻车的地方如果你登录MySQL的时候提示要初始化validate_password组件或者你改了密码之后提示不符合安全强度那就不是认证协议的问题了。MySQL 8.0自带validate_password组件时密码长度和复杂度有硬性要求。可以用这条查看当前策略SHOW VARIABLES LIKE validate_password%;常见的拦截原因是密码长度不足8位或者没有大小写字母/数字混排。我之前帮朋友处理问题就是卡在这一步他设了个123456结果ALTER命令直接报错。解决办法很简单把密码设复杂一些例如MySql2024这种长度不少于8位、带大小写和特殊字符的密码。如果你的测试环境确实不想要这个限制也可以临时卸载组件但我不建议在生产环境这么干安全底线还是别轻易降。3.5 还有一个全局配置参数可以绕行在MySQL 8.0早期版本还可以在my.cnf或my.ini里设置全局默认认证插件[mysqld] default-authentication-pluginmysql_native_password设置之后新创建的用户默认就走旧协议。这个方法当年很流行但注意MySQL 8.0.26版本里这个参数被标为废弃8.0.27之后被移除了新版本再写这个配置会被忽略甚至导致启动报错。所以如果你用的是8.0.27以上的版本就不要指望这个参数了老老实实走ALTER或新建账号的路子。4. 高频坑与排查技巧实录4.1 改完插件仍然报错是怎么回事遇到过好几次改了认证插件查询mysql.user也确实变成了mysql_native_password但客户端还是报同样的错。排查下来原因集中在下面几个点改错了host最常见。连接用的host是%你改的是localhost两边对不上。记住一条用命令查询时user和host是一对组合键缺一不可。连接串缓存有些程序、IDE工具会缓存连接元数据改完插件后需要重启工具或刷新连接池我遇到过DBeaver缓存的连接信息没刷新的情况重启之后就好了。代理层和中间件如果你的应用通过ProxySQL、MyCat这类中间件连MySQL中间件会保存自己的会话状态改了后端账号的插件中间件连接池里的旧会话可能还是连不上需要刷新或重启中间件连接池。排查思路我是这样走的先用命令行模拟TCP连接排除数据库侧问题再用客户端工具直连排除中间件问题最后再往连接串缓存上查。4.2 Docker环境下MySQL 8.0的特别说明如果你用的是Docker安装MySQL 8.0修改认证插件的思路完全一样但有几个操作层面上的区别。进入容器docker exec -it mysql-container mysql -uroot -p然后执行同样的SQL命令。但要注意Docker容器里的MySQL配置文件和数据都在容器里容器一旦被销毁重建你改过的账号信息就全没了。所以如果你计划用传统方式修改认证插件一定要记得把数据目录挂载出来或者把修改操作写进初始化脚本里否则每次重建容器都要重来一遍。更推荐的做法是在启动容器时就加上默认认证插件的参数比如docker run --name mysql8 \ -e MYSQL_ROOT_PASSWORDyourpass \ -p 3306:3306 \ mysql:8.0 \ --default-authentication-pluginmysql_native_password但前面也提到这个参数在高版本里被移除了所以你如果用8.0.27以上的镜像这个启动参数也不管用只能进容器里执行ALTER。实际项目中更省心的方式是用docker-compose配合挂载初始化SQL脚本在容器第一次启动时自动执行ALTER USER这样每次重建容器都不用担心。4.3 连接串和驱动版本一定要核对有时候问题压根不在认证插件而是驱动版本没选对。以Java为例即使你把账号改成了mysql_native_password如果你的mysql-connector-java还是5.x连MySQL 8.0会碰上新的问题比如时区报错、SSL握手失败。所以建议直接升级到8.0的驱动并在连接串里带上关键参数jdbc:mysql://127.0.0.1:3306/mydb?useSSLfalseallowPublicKeyRetrievaltrueserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue这个参数很关键。在用caching_sha2_password协议时客户端连接需要从服务器获取公钥如果客户端工具默认不信任这个获取过程就会抛出公钥检索相关的错误。如果你不想改动数据库账号的插件只想让程序继续连8.0那加这个参数常常能解决握手问题。Python那边也一样pymysql升级到1.0或者改用mysqlclient基本不会再出这个报错。4.4 别一上来就动root账号这是我最想强调的一点。很多人一看报错第一反应就是把root改成mysql_native_password。但root不是普通的业务账号它是数据库的超级管理员把它的认证强度降下来等于给整个数据库开了个口子。尤其在生产环境我更倾向于新建一个专用账号比如dev_app只授权到它需要的那几个库。操作上这是最安全的路径CREATE USER dev_app% IDENTIFIED WITH mysql_native_password BY StrongPass123; GRANT SELECT, INSERT, UPDATE, DELETE ON myapp.* TO dev_app%;如果你的项目还需要执行DDL比如自动建表那就把权限加上CREATE和ALTER。但别给SUPER、GRANT OPTION这类高危权限能用最小权限跑起来就别手滑给多了。4.5 排查思路速查表我把这个问题的排查过程整理成一张表方便你直接对照报错现象可能原因排查步骤解决办法Client does not support authentication protocol客户端不支持caching_sha2_password查询mysql.user确认插件改插件或升级客户端Access denied ... using password: YES密码错误或权限问题确认密码、host、授权重置密码或授予权限Public Key Retrieval is not allowed连接串缺少公钥允许检查JDBC连接串参数加allowPublicKeyRetrievaltruealter user失败 密码策略不通过validate_password组件拦截查看validate_password变量设置符合策略的强密码最后说点实在的这个报错看着吓人实际就是一个协议握手问题理解了原理之后解决起来并不复杂。我个人在实际操作中体会最深的一点是遇到兼容性问题先想清楚你要迁就谁。如果数据库用户少、开发环境随意改插件最省事如果是有线上流量的系统宁可多花十分钟新建一个专用账号也别轻易把root拉低到旧协议。另外很多工具和驱动更新频繁这个报错在2024年之后的版本里已经越来越少见因为主流客户端基本都兼容了新协议如果你手上全是新版本还撞上这个错那大概率不是协议问题而是连接串参数或者host不匹配沿着上面的排查表走一遍很快就有结果。最后再提醒一句以后新建MySQL 8.0用户尽量直接指定caching_sha2_password让新用户默认走新协议从源头减少后续的兼容性麻烦。
返回列表