ARTICLE DETAIL

资讯详情

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

MySQL 8.0认证报错1251解决方案:从caching_sha2_password到mysql_native_password的完整指南

MySQL 8.0认证报错1251解决方案:从caching_sha2_password到mysql_native_password的完整指南 一个老生常谈但每次遇到都得翻一下文档的问题。连MySQL的时候客户端直接甩一句1251 - Client does not support authentication protocol requested by server多半是客户端和服务器之间在认证协议上“语言不通”。尤其是从MySQL 8.0开始默认认证插件改成了caching_sha2_password很多老版本客户端比如PHP 7.1之前、MySQL 5.x时代的客户端库、部分旧版Navicat根本不认识这个协议连接自然被掐断。这篇文章就专门拆这个问题把报错原因、解决思路、实际可操作的步骤和典型的坑都过一遍不管是刚装完MySQL的新手还是负责维护老项目的熟手都能直接拿来参考。1. 认识这个报错故障现场与问题本质1.1 报错出现的典型场景这个错误最常见的出现时机就是安装完MySQL 8.0之后拿着老工具去连。比如用Navicat 12以前的版本连本地MySQL 8.0新建连接点“测试连接”秒报1251。PHP项目用mysqli或PDO连接页面直接抛SQLSTATE[HY000] [2054] The server requested authentication method unknown to the client。Python脚本用mysql-connector-python老版本或者PyMySQL配置不当连库失败。某些Linux发行版自带的mysql-client其实是MariaDB客户端去连MySQL 8.0也会出现同类提示。不管表现形式是1251还是2054根子都是一样的客户端支持的认证插件列表里没有服务器要求的那一个。1.2 为什么MySQL 8.0要换认证插件MySQL 5.7及更早版本默认用的是mysql_native_password这是一种基于SHA1的密码散列方案优点是简单、兼容性极好几乎所有客户端都支持。缺点是安全性偏弱主要体现在当客户端和服务器通信时如果连接通道没有加密比如没走SSL或者不处于localhost安全环境密码散列可能会被中间人捕获存在字典攻击的风险。MySQL官方在8.0把默认认证插件换成了caching_sha2_password。这个新插件使用SHA256加密算法并且内置了一个缓存机制对于认证过的用户后续连接可以走缓存的快速路径安全性相比mysql_native_password有了明显提升。问题是这个插件是8.0才正式全面推行很多旧客户端根本没有适配。于是服务器说“我们要用caching_sha2_password”客户端说“我听不懂”连接就这样中断了。1.3 理解用户与认证插件的关系在MySQL里每个用户帐号在mysql.user系统表中都有一行记录其中有一个plugin字段决定这个用户登录时使用哪种认证方式。默认情况下8.0创建的用户plugin会自动设为caching_sha2_password。如果你想让某个用户用旧协议只需要把这行的plugin改掉就行。可以把这个关系想成一把锁和钥匙服务器上的用户帐号是锁客户端是钥匙。现在锁换成了新型防盗锁caching_sha2_password而钥匙还是旧模具做的mysql_native_password自然是插不进去、打不开的。解决问题的核心就是要么把锁换成旧款要么给钥匙换成新模具。2. 快速解决方案把用户认证插件改回旧协议2.1 适用场景与选择依据这个方案适用于只有个别用户需要被旧客户端连接或者想最快速度让手头的工具赶紧连上。改动小、见效快一条SQL就能解决。改动范围只针对单个用户不会影响其他用户和整个MySQL实例的全局配置风险极低。如果你连接的是自己的本地开发库压根不想折腾全局配置直接用这个方法。要注意的是改完之后这个用户后续与服务器之间的密码验证方式就退回到SHA1算法安全性比新插件弱一些。但在内网、本地开发环境中这种取舍基本可以接受。2.2 操作步骤全流程第一步用命令行工具以有权限的账号登录MySQL。如果你现在还能通过本地socket方式登录直接执行mysql -u root -p输入root密码进入MySQL命令行。如果root本身也已经被设成了新认证插件而你手里的客户端又太旧连不进去可以临时用skip-grant-tables模式启动MySQL跳过权限认证。这个属于紧急救援手段后文会单独讲。第二步查看当前用户的认证插件。SELECT user, host, plugin FROM mysql.user WHERE user 你的用户名;比如查看rootSELECT user, host, plugin FROM mysql.user WHERE user root;返回结果里能看到root对应的plugin。如果显示caching_sha2_password那这就是报错的直接原因。第三步修改用户的认证插件和密码。执行如下SQLALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 你的密码;这段命令的含义是把rootlocalhost这个用户的认证方式改为mysql_native_password并重新设置密码。注意这里的密码是明文写的SQL语句执行后MySQL会自动计算并保存新的密码散列值。如果你只想改插件、保持原密码不变MySQL也支持下面这种写法ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password;不过实测时这种写法在部分版本上可能会因为密码缺失而报错保险起见还是用BY 密码把密码一并写清楚。第四步刷新权限使其立即生效。FLUSH PRIVILEGES;虽然ALTER USER执行后不需要强制刷新权限但加上这一步可以确保后续连接请求完全基于最新权限表避免遇到权限缓存类问题。第五步重新用客户端连接测试。用原来的客户端再次连接应该就能进去了。如果还是报错检查一下你连接时用的用户是不是rootlocalhost这个帐号有可能你实际连接的是root%而它仍然保留着新认证插件。这种情况很常见需要把host也带上精确匹配再改一次。2.3 多用户批量处理的写法如果项目里有多个用户都需要被旧客户端访问不需要一个个手动改。可以用存储过程动态生成并执行ALTER USER或者干脆用一条查询拼出所有SQL语句再复制执行。查询拼接是最直观的SELECT CONCAT(ALTER USER , user, , host, IDENTIFIED WITH mysql_native_password BY 你的密码;) FROM mysql.user WHERE plugin caching_sha2_password;执行后会得到一系列ALTER USER语句把结果复制出来逐条执行即可。注意你的密码这里需要你自己替换成统一的密码如果你希望保留每个用户原本的密码这种拼接方式是做不到的只能用存储过程配合SET PASSWORD变量方式处理。不过话说回来生产环境中批量改密码这种行为本身就带有一定风险建议提前确认好业务影响范围。2.4 修改后的验证方法改完别急着关命令行验证一下连接是否真的稳定SHOW VARIABLES LIKE default_authentication_plugin;这个变量反映的是MySQL实例默认的认证插件。如果你只是修改了单个用户这个变量依然显示caching_sha2_password这是正常的。只有当你新建用户时会默认使用这个全局变量指定的插件。所以即使全局变量保持不变你修改过的那个用户已经可以正常被旧客户端连接了。3. 全局兜底方案调整MySQL默认认证插件3.1 什么情况下需要全局配置如果你的MySQL服务器要给多套旧系统共用或者你不想每一次创建新用户后都手动去改插件那么直接在MySQL配置层面把默认认证插件换成mysql_native_password会是更省心的做法。这个方案的优点是一劳永逸之后所有新建用户都会自动使用旧协议。缺点也很明显服务器整体认证强度会回退到MySQL 5.7时代对于安全性要求极高的生产环境不太推荐。适用场景包括本地开发环境只图顺手快捷。内网测试服务器客户端工具版本普遍偏旧统一改造客户的成本太高。运维人员少不想给每个新用户单独设置认证插件。3.2 如何修改配置文件MySQL 8.0在Linux上默认读取的配置文件是/etc/my.cnf在Windows上则是my.ini。用编辑器打开在[mysqld]段落内加入一行[mysqld] default_authentication_pluginmysql_native_password如果没有[mysqld]段落手动加一个即可。注意这里是在服务器端配置不是在客户端配置千万别写到[client]或者[mysql]标签下面否则不会生效。保存文件后重启MySQL服务# 使用systemd的系统 systemctl restart mysqld # 使用sysvinit或service命令的系统 service mysqld restart重启之后再执行SHOW VARIABLES LIKE default_authentication_plugin;返回值应该已经是mysql_native_password。3.3 这个方案对已有用户是否生效这里是个很容易踩的坑。修改全局默认认证插件不会改变已有用户的认证方式。就算你在配置里指定了mysql_native_password之前已经被创建成caching_sha2_password的用户依然保持原样。对于他们你仍然需要手动执行ALTER USER来修改。也就是说全局配置只能管“以后新建的用户”。已有用户该单独改还得改。这也是为什么很多新手改了配置、重启了服务还是连不上——因为没有处理存量用户。在动手前最好先梳理一下当前实例下有哪些用户用前面提到的SELECT user, host, plugin FROM mysql.user;查一遍把存量用户都标注出来再决定是否走全局方案。3.4 配置完成后新建用户的验证配置好全局插件后新建用户测试一下效果CREATE USER tester% IDENTIFIED BY Test123;然后查询SELECT user, host, plugin FROM mysql.user WHERE user tester;正常情况下plugin是mysql_native_password。这时候再用旧客户端连接tester应该畅通无阻。4. 不同工具和场景下的实战处理4.1 Navicat连接报1251的完整处理Navicat在Windows下连接MySQL 8.0时如果版本低于12误报概率极高。处理思路分两步第一步用命令行如mysql -u root -p进入MySQL查看并修复用户认证插件ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 你的密码; FLUSH PRIVILEGES;第二步在Navicat中重新编辑连接在“高级”或“SSH”选项卡中不需要额外调整只要上一步改完直接测试连接即可。如果Navicat版本实在太老连mysql_native_password都识别不了那只能升级Navicat到高版本这是最省事的。4.2 PHP项目连接报错的排查方法PHP连接MySQL 8.0常见的报错是SQLSTATE[HY000] [2054] The server requested authentication method unknown to the client这个错误和1251本质相同只是另一种表述。PHP环境下的处理核心同样是把MySQL用户改成mysql_native_password。但还有一个隐藏点PHP的mysqlnd扩展版本太老即便用户认证插件改成了mysql_native_password也可能因为默认字符集或权限问题继续报错。建议同时检查PHP版本尽量使用PHP 7.2以上版本并确保php-mysql扩展版本与MySQL 8.0兼容。在PHP 5.6等老版本环境中旧客户端算法已经过时即使改成mysql_native_password也未必能成功升级PHP是更彻底的办法。另外PDO连接时也可以在DSN中主动指定字符集减少因字符集协商引发的次生问题$pdo new PDO(mysql:host127.0.0.1;port3306;dbnametest;charsetutf8mb4, user, password);4.3 Python连接MySQL的解决方案Python的常用库包括PyMySQL和mysql-connector-python。对于MySQL 8.0PyMySQL从0.9.3版本开始就支持caching_sha2_password但前提是cryptography包要安装到位。如果连接时报错Authentication plugin caching_sha2_password cannot be loaded可以先用pip install cryptography来补齐依赖。如果还是不放心也可以直接把用户改成mysql_native_password然后继续使用老版本PyMySQL两条路都通。在写连接代码时可以显式指定认证插件import pymysql conn pymysql.connect( host127.0.0.1, userroot, passwordpassword, charsetutf8mb4, auth_pluginmysql_native_password )这个参数告诉PyMySQL使用旧协议进行认证避免客户端去尝试不支持的插件。4.4 JDBC连接MySQL 8.0的注意点Java应用使用JDBC连接MySQL 8.0通常用的是com.mysql.cj.jdbc.Driver驱动。JDBC驱动8.0版本本身是支持caching_sha2_password的但如果你使用的驱动版本偏旧比如5.1.x同样会报认证协议不支持。解决办法是把MySQL Connector/J驱动升级到8.0.x或者在JDBC URL中添加参数jdbc:mysql://127.0.0.1:3306/test?useSSLfalseserverTimezoneUTCallowPublicKeyRetrievaltrueallowPublicKeyRetrievaltrue这个参数挺关键。当客户端走caching_sha2_password认证且没配置SSL时需要从服务器获取公钥来加密密码传输。JDBC驱动默认禁止自动获取公钥所以要把这个选项打开。当然也可以把用户改成mysql_native_password来规避这个参数。如果你的用户已经改成旧协议URL里就不需要allowPublicKeyRetrievaltrue了。4.5 Docker容器中MySQL的认证配置用Docker跑MySQL 8.0修改默认认证插件需要在容器启动时追加参数docker run -d \ --name mysql8 \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORDroot123 \ -e MYSQL_DATABASEtest \ mysql:8.0 \ --default-authentication-pluginmysql_native_password注意在MySQL 8.0的镜像中--default-authentication-plugin参数在较新版本比如8.0.34之后的镜像可能已经被标记为废弃但依然可用可以正常设置。如果使用的是MySQL 8.4镜像这个参数可能已经不再支持需要使用新的配置方式或者在容器启动后再执行SQL修改用户。容器启动后如果仍然报1251大概率是容器内的MySQL已经把root用户创建成了caching_sha2_password。此时进入容器手动修改docker exec -it mysql8 mysql -uroot -proot123然后在SQL里执行ALTER USER root% IDENTIFIED WITH mysql_native_password BY root123; FLUSH PRIVILEGES;注意root的host可能是%如果允许远程连接或者localhost需要根据实际情况确认。5. 常见问题与排查技巧实录5.1 修改插件后仍然无法连接遇到这种情况先不要盯着认证插件不放按顺序排查下面几个点用户对应的host是否匹配连接时MySQL会按user和host两列做匹配如果你连接的是rootlocalhost但只改了root%的插件自然不生效。用命令SELECT user, host, plugin FROM mysql.user WHERE userroot;查看所有host记录。客户端是否真的走了MySQL协议有些工具支持通过SSH隧道连接连接时可能会隐藏在隧道的另一端需要确认目标MySQL实例确实是8.0。是否存在本机端口映射混乱如果本机装了多个MySQL实例或者Docker端口映射到了别的端口你连的可能不是你以为的那个实例。用SHOW VARIABLES LIKE port;和SELECT VERSION();确认当前连的服务器版本。5.2 忘记root密码且旧客户端连不上时怎么办这是最具挑战性的场景你用旧客户端连不上MySQL 8.0又忘了root密码连进服务器命令行都做不了。这时需要用免认证模式启动MySQL停止MySQL服务。在配置文件[mysqld]下添加一行skip-grant-tables重启MySQL服务。此时任何客户端都可以免密登录执行mysql -uroot注意skip-grant-tables模式下某些版本还会额外跳过权限表的加载ALTER USER可能报错因为权限系统处于特殊状态。常见处理方式是先刷新权限让认证系统恢复加载FLUSH PRIVILEGES;然后重新设置root密码和认证插件ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY new_password; FLUSH PRIVILEGES;改完后删掉配置文件中的skip-grant-tables行重启服务用新密码连接。这个操作非常敏感在免认证模式下任何人只要能连到MySQL端口就可以拿到全部数据。千万不能在生产环境中开着这个配置长时间运行。改完密码后一定要立即恢复配置文件。5.3 密码包含特殊字符时的坑执行ALTER USER ... BY 密码时如果密码中带有单引号需要转义。比如密码是abc123应该写成ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY abc123;另外密码中使用、$等符号本身没有问题只要整个密码串被单引号包裹即可。在命令行里如果密码包含!或$有可能被shell解释建议在MySQL命令行交互窗口输入而不是直接通过mysql -e ALTER USER ...传递。5.4 8.0之后创建用户与授权的最小范例顺带提一下MySQL 8.0中创建用户和授权是完全分开的两个语句。早期MySQL版本中常用的GRANT ALL ON *.* TO user% IDENTIFIED BY password;在8.0已经不可用了。正确姿势是CREATE USER user% IDENTIFIED WITH mysql_native_password BY password; GRANT ALL PRIVILEGES ON db_name.* TO user%; FLUSH PRIVILEGES;很多报错其实是误以为授权失败导致的混乱确认这两步都执行成功后再去测连接。5.5 关于SSL与认证协议的纠缠MySQL 8.0的caching_sha2_password在非SSL连接下需要额外的RSA密钥交换步骤。某些客户端在连接时会明确提示需要服务器公钥比如JDBC的Public Key Retrieval is not allowed。如果看到类似提示说明不是认证插件不匹配而是客户端拿到了新插件但卡在了密钥获取环节。处理方法有两个方向走SSL连接证书配置好之后公钥步骤自动省略。在客户端连接参数中允许公钥获取比如JDBC加allowPublicKeyRetrievaltrue。这个问题与1251并不相同但经常被人混在一起排查。区分方法很简单1251是“客户端根本不知道服务器要求的协议”后者是“客户端认得协议但缺少交换密钥的许可”。5.6 排查问题时的快速命令集合为了方便以后排错把这组常用命令存在这里遇到同类问题直接复制执行-- 查看所有用户及其认证插件 SELECT user, host, plugin FROM mysql.user; -- 查看当前默认认证插件 SHOW VARIABLES LIKE default_authentication_plugin; -- 查看当前用户实际连接使用的帐号 SELECT CURRENT_USER(); -- 修改用户认证方式最常用 ALTER USER 用户名主机 IDENTIFIED WITH mysql_native_password BY 新密码; -- 刷新权限 FLUSH PRIVILEGES;5.7 我踩过的一个隐蔽坑忘记刷新连接池这是个超级容易被忽略的环节。你改了用户认证插件命令行测试能连上但应用还是持续报错。折腾了半天最后发现是应用侧的连接池缓存了旧的失败连接。比如HikariCP、Druid这类连接池在初始化时可能会记住早期连接失败的错误状态需要重启应用或清空连接池才能恢复。遇到过不止一次SQL层面没问题、客户端版本没问题但就是连不上重启一遍Java服务就好了。所以改完数据库侧配置后如果应用仍然报错优先看连接池是否需要释放和重建。写在后面的一些体会关于1251这个报错网上解决方案铺天盖地无非是ALTER USER加mysql_native_password但真正难的不是命令本身而是理解这套认证机制的变化脉络。MySQL 8.0替换默认认证插件是出于安全考量是在推动整个生态往前走但这种推进注定要和大量历史版本的客户端产生摩擦。作为使用者一方面可以用mysql_native_password快速平复问题另一方面也要清楚这是一种“兼容性降级”。如果业务对安全有硬性要求更值得做的其实是升级客户端库而不是惯着老协议。就我个人经验而言处理这类问题最好的方式是把变更记录写清楚。哪一天、哪个用户、从什么认证插件改成什么为什么改都要留个底。否则过了半年你自己回头看也说不清楚为什么有些用户是旧协议、有些是新协议。最后提醒一下如果条件允许尽早把项目里的客户端组件升级到支持caching_sha2_password的版本这才是符合MySQL长期发展方向的解法。
返回列表