真是被这个报错折磨过的人,一看这个英文就能想起当时的心情。大半夜连个本地数据库,结果Navicat弹出一行红字“1251 - Client does not support authentication protocol requested by server”,翻译过来就是:客户端不支持服务端要求的认证协议。我第一次遇到时还以为是密码输错了,反复确认了好几次,最后才发现根本不是密码的问题,而是MySQL 8.0之后在认证方式上动了一次大手术。这篇文章就把这个问题彻底讲透,从原因到解决方案,再到我踩过的坑,一次性说清楚。
这个报错的核心只有一个:MySQL 8.0默认使用的认证插件是caching_sha2_password,而你手上这个客户端(Navicat旧版、老版本的JDBC驱动、某些老PHP项目里的mysqli扩展)只认mysql_native_password。两边语言不通,服务器直接就拒绝了。适合看这篇文章的人包括:刚装完MySQL 8.x准备用Navicat连接的新手、接手老项目被数据库连接问题卡住的开发、以及所有被这个1251错误搞到头秃的朋友。下面我按“为什么会这样、怎么排查、怎么解决、后续怎么避免”这个顺序来写。
1. 先说清楚这个报错到底在说什么
1.1 报错出现的典型场景
我不止一次在技术群里看到有人发这个报错截图,场景几乎一模一样:MySQL服务端装的是8.0或更高版本,客户端用的是Navicat 11或者更老的版本、SQLyog旧版、老项目里引入的mysql-connector-java 5.x驱动,又或者是Workbench 8.0之前的版本。连接一建立,服务端发来一个“打招呼”的握手包,里面写着“我支持这些认证插件”,客户端一看,霍,里面没有自己能用的,于是报错。
另外还有一个场景容易被忽略:用了连接池中间件,比如Druid、HikariCP,即使应用本身没问题,中间层拿到连接时握手失败,同样会抛出这个错。遇到报错先别急着重装MySQL,绝大部分情况下你的数据库本身是好的,数据也没丢,纯粹是认证方式不匹配的问题。
1.2 关键问题出在认证插件上
MySQL 5.7及更早的版本里,默认的认证插件是mysql_native_password,它做的事情很简单:把用户密码做一次SHA1哈希,存到mysql.user表里。客户端连接时,服务端发一个随机数,客户端用密码哈希加上这个随机数再做一次哈希返回,服务端校验通过就放行。这套流程跑了很多年,几乎所有客户端和驱动都支持。
到了MySQL 8.0,官方把默认认证插件换成了caching_sha2_password。这个新插件的安全性更好,使用SHA256加密,还加了一个缓存机制来提升认证速度。但问题在于,它是8.0之后才出现的,旧版客户端根本不认识它。就相当于你换了一把新锁,但手里还是旧钥匙,钥匙插进去转不动,门自然不会开。更麻烦的是,MySQL 8.0在安装时默认对所有新创建的用户使用新插件,这就导致大量老客户端直接中招。
1.3 为什么报错信息没有直接告诉你“密码错了”
有朋友会问,既然认证失败,那为什么报错信息不是“Access denied”而是“not support authentication protocol”?原因是两者的失败阶段不一样。
“Access denied”发生在认证过程中,代表密码校验失败;而1251这个报错发生在握手阶段,客户端还没走到“校验密码”那一步就已经被否了。服务端的意思是:我只会用caching_sha2_password这种方式跟你认证,但你连这种认证方式都不支持,咱俩没法继续聊了。理解这一点很重要,因为你排查问题时一旦想明白“密码根本没参与校验”,就不会再傻傻地改密码了。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 排查这一步其实很简单,但很多人跳过
2.1 先确认服务端MySQL版本
我见过有人拿着MySQL 5.6的报错来问为什么也会有1251,这种情况其实不太常见,但也确实存在。所以第一步,确认版本。用命令行登录MySQL(如果有权限的话),执行:
sql复制SELECT VERSION();
如果看到8.0.x,那基本可以确定就是认证插件的问题。如果版本是5.7以下却报了1251,那问题可能出在连接方式上,比如用了SSL相关参数但服务端不支持,或者客户端负载均衡配置有误。不过90%以上的情况都是8.0版本引发的。
2.2 找出到底是谁不支持
这个报错里的“client”不一定是你的数据库管理工具,也可能是几层不同的东西:
- 如果你用Navicat连报错,问题在Navicat本身
- 如果你用JDBC连报错,问题在mysql-connector-java的版本
- 如果你用Python的pymysql或MySQLdb连报错,问题在驱动库
- 如果你用Docker容器里的服务去连宿主机的MySQL报错,问题可能在容器内的客户端版本
我在实际排查中发现,用Spring Boot项目连MySQL 8.0时报这个错的,绝大多数是maven依赖里的mysql-connector-java还是5.1.x版本。项目能编译能启动,但一旦真正建立数据库连接就抛异常。这种问题升级依赖版本就能解决。
2.3 检查目标用户当前的认证插件
如果MySQL服务端能进,直接查一下你连接时用的那个用户到底是什么认证插件。执行:
sql复制SELECT user, host, plugin FROM mysql.user WHERE user = 'root';
也可以把连接所用的IP和用户名都查一下,比如:
sql复制SELECT user, host, plugin FROM mysql.user WHERE user = 'yourname' AND host = '%';
查到结果后,如果plugin一列显示的是caching_sha2_password,而你的客户端不支持,那接下来要做的事情就很明确了:把它改成mysql_native_password,或者把客户端升级到支持新插件的版本。
3. 解决方案,按场景挑一种就行
3.1 方案一:修改用户认证插件(最推荐,改动最小)
如果你是本地开发环境,或者测试服务器,最直接的办法就是把连接用户的认证方式改回旧的mysql_native_password。操作不复杂,三步搞定。
先用root登录MySQL:
bash复制mysql -u root -p
然后执行:
sql复制ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '你的密码';
FLUSH PRIVILEGES;
注意,如果用的host是%或者某个具体IP,那就要把'localhost'换成对应的host字段值。比如用Navicat远程连接,连的可能是'root'@'%',那就改成:
sql复制ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '你的密码';
FLUSH PRIVILEGES;
FLUSH PRIVILEGES不是必须的,因为ALTER USER本身会更新权限表,但多执行一次没有坏处。改完后再用客户端连接,1251报错就消失了。
这个方法只影响这一个用户,其他用户仍然是caching_sha2_password,安全性上没有大范围回退,所以在需要兼容老客户端的场景下,这是最合适的解法。缺点是,如果你有十来个用户都要连,就得挨个改,容易漏。
3.2 方案二:修改全局默认认证插件
如果你要兼容的客户端特别多,或者手头有几十个用户,一个个改太麻烦,那可以直接在配置文件里把默认认证插件改回旧版。MySQL的配置文件在Linux下通常是/etc/my.cnf或/etc/mysql/mysql.conf.d/mysqld.cnf,Windows下是my.ini。
在[mysqld]下面加一行:
ini复制[mysqld]
default_authentication_plugin=mysql_native_password
保存后重启MySQL服务:
bash复制systemctl restart mysqld
重启之后,新创建的用户就会默认使用mysql_native_password插件。但这里有个坑:已经存在的用户不会自动切换,你仍然需要手动执行ALTER USER来改。所以这个方法适合“还没建用户”或者“准备新建一批账号”的场景,对存量用户无效。
另外,注意MySQL 8.4开始,官方已经标记mysql_native_password为废弃插件,默认编译里可能直接不包含。所以在8.4以上的版本里,这个方案可能不生效。建议只在8.0.x版本上用。
3.3 方案三:升级客户端或驱动(治本,推荐)
前面两种方案都是让MySQL“迁就”旧客户端,属于临时方案。长期来看,最健康的做法是把客户端升级到支持caching_sha2_password的版本。
- Navicat:升级到12.0.22以上,或者直接用16.x版本
- mysql-connector-java:升级到8.0.x以上,比如8.0.33
- pymysql:升级到0.9.3以上
- mysql2(Node.js):升级到2.0.0以上
- Workbench:直接装8.0版本
升级驱动时要顺便看一眼依赖冲突。比如Java项目里,如果其他库传递依赖了旧版驱动,你光改pom.xml可能没效果,要用mvn dependency:tree排查一下。我遇到过明明在pom里写了8.0.33,结果项目跑起来还是用了一个5.1.47的老驱动,查半天才知道是某个框架的starter里锁了版本。
3.4 三种方案怎么选,给你一个判断清单
我这几年处理下来,基本遵循以下原则:
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| 本地开发快速连库 | 方案一 | 一行SQL搞定,不碰配置文件 |
| 老项目急着上线 | 方案一 | 改动最小,风险可控 |
| 新部署8.0,客户端统一 | 方案三 | 从源头避免问题 |
| 大量用户都要支持老客户端 | 方案二 | 新建用户默认旧插件 |
| 生产环境谨慎操作 | 方案三为主 | 避免降低整体安全水位 |
总的来说,如果你是在自己的开发机上折腾,用方案一和方案三都行。如果是在生产环境,我强烈建议不要为了省事把全局认证插件改回旧版,尤其是在等保、合规要求比较严的场合,默认的caching_sha2_password是更安全的选择。
4. 动手实操:从报错到连上,完整复现一遍
4.1 一个典型的处理过程
假设你现在的情况是:Windows本机装的是MySQL 8.0.36,Navicat 15连接报1251错误。步骤拆开看就是这样。
第一步,打开命令行,进入MySQL安装目录下的bin目录,用root登录:
bash复制mysql -u root -p
输入密码后进入MySQL命令行。执行:
sql复制SELECT user, host, plugin FROM mysql.user WHERE user = 'root';
你大概率会看到类似这样的结果:
text复制+------+-----------+-----------------------+
| user | host | plugin |
+------+-----------+-----------------------+
| root | localhost | caching_sha2_password |
+------+-----------+-----------------------+
看到caching_sha2_password,病因实锤。
第二步,执行修改命令:
sql复制ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '123456';
FLUSH PRIVILEGES;
注意,密码部分要替换成你自己的密码。这里有个细节:如果之前已经设置过复杂密码,又想保持密码不变,可以用这样一个写法:
sql复制ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password;
它表示“保持当前密码不变,只改认证插件”。这个写法在MySQL 8.0.23以上版本测试有效,可以省去重新输入密码的麻烦。
第三步,回到Navicat重新连接。如果一切正常,连接成功,问题解决。如果还报错,检查一下Navicat连接配置里的端口对不对(默认3306),以及有没有勾选“使用SSL”之类的选项。有些MySQL配置了require_secure_transport=ON,会导致非SSL连接被拒绝,但那种情况下报错一般不是1251,而是SSL相关的错误。
4.2 Docker环境的特殊处理
现在很多朋友习惯用Docker跑MySQL,这个场景下更容易遇到1251,因为官方镜像默认就是8.0,而且容器内的配置你可能根本不想动。
处理逻辑还是那几招,但操作入口变了。先用docker exec进入容器:
bash复制docker exec -it mysql8 mysql -u root -p
然后执行同样的ALTER USER语句。如果你是在docker run时就想避免这个问题,可以在启动命令里加上参数:
bash复制docker run -d \
--name mysql8 \
-p 3306:3306 \
-e MYSQL_ROOT_PASSWORD=123456 \
-e MYSQL_ROOT_HOST=% \
mysql:8.0 \
--default-authentication-plugin=mysql_native_password
注意最后这个参数,它是作为mysqld的启动参数传进去的。用docker compose的话,在command字段下写:
yaml复制services:
mysql:
image: mysql:8.0
command: --default-authentication-plugin=mysql_native_password
environment:
MYSQL_ROOT_PASSWORD: "123456"
MYSQL_ROOT_HOST: "%"
但正如前文所说,这个参数只影响新建用户,存量用户改起来还是得进容器操作。所以如果你已经启动了容器,最省事的还是用ALTER USER。
4.3 验证连接成功的三个细节
改完认证插件后,我建议做三件事,确保后面不会出幺蛾子:
第一,重新查询一下用户表,确认plugin字段已经变成mysql_native_password:
sql复制SELECT user, host, plugin FROM mysql.user WHERE user = 'root';
第二,用一个全新的连接串测试。比如用命令行客户端连一次:
bash复制mysql -h 127.0.0.1 -P 3306 -u root -p
注意这里要用-h指定IP,而不是直接连socket,因为Navicat走的是TCP,和本地socket登录在认证路径上有细微差别。
第三,如果项目里配了连接池,重启一下应用,观察启动日志里的数据库初始化部分,确认不会抛出The server requested authentication method unknown to the client之类的异常。
这三个步骤看起来简单,但能帮你区分“数据库层面已经解决”和“客户端缓存导致看起来没解决”。Navicat有时候会缓存连接信息,改完配置后建议把连接关掉重开,而不是一直点“测试连接”。
5. 常见问题速查与避坑经验
5.1 一个对照表,解决大部分疑问
| 问题表现 | 可能原因 | 解决动作 |
|---|---|---|
| Navicat 11连8.0报1251 | Navicat太老 | 升级Navicat或改用户插件 |
| JDBC连8.0报1251 | connector版本太低 | 升级驱动到8.0.x |
| pymysql连8.0报错 | pymysql版本低于0.9.3 | 升级pymysql |
| Docker里改了用户仍报错 | host字段不匹配 | 查询user表确认host值 |
| 改了全局默认插件无效 | 存量用户未变 | 手动ALTER USER逐个改 |
| 8.4版本找不到旧插件配置 | 官方已废弃该参数 | 升级客户端,别改服务端 |
5.2 几个容易踩的坑
第一个坑是host匹配问题。我见过一个朋友,ALTER USER执行了,返回Query OK,但连接还是报错。后来一查,连接用的host是% ,但他改的是localhost。MySQL里的用户是由“用户名+host”共同确定的,两者不一致就相当于改了别人,和你无关。
第二个坑是密码复杂度策略。MySQL 8.0默认装了validate_password组件,你如果想把密码改成123456,可能会碰到“Password does not satisfy the current policy requirements”的报错。这种情况下要么设一个包含大小写数字和特殊字符的密码,要么临时卸载这个组件。建议用合法密码,不要为了测试把安全策略拆了。
第三个坑是改完插件后,其他应用连不上了。比如同时有老客户端和新客户端在连同一个用户,你把认证插件改成旧版,新客户端本来支持新版,现在也照样能连,因为mysql_native_password是被广泛支持的。所以把单个用户改成旧插件,兼容性上问题不大,反而是反过来(把全局改成新插件)容易出兼容性问题。
第四个坑是关于MySQL 8.4及更新版本。在这个版本里,官方已经连mysql_native_password插件本身都开始弱化了,8.4开始默认禁用,9.0直接移除。所以如果你用的是高版本MySQL,与其纠结怎么开旧插件,不如老老实实升级客户端。具体来说,MySQL 8.4里Caching_sha2_password已经成为了唯一默认认证插件,还想用mysql_native_password就得在配置里显式启用,非常麻烦。
5.3 我的一点经验之谈
处理过太多次1251之后,我个人的习惯是:数据库服务端是8.0或以上的,一律把客户端和驱动升级到位,而不是去动数据库的认证配置。原因有两个。第一,caching_sha2_password的安全性确实比mysql_native_password好,没必要为了兼容老工具拉低安全水位。第二,改了认证插件只是解决眼前问题,下次换个工具连又报错,治标不治本。
如果是那种公司内部老旧系统、驱动版本动不了、硬件环境又没法随意升级的情况,那就用ALTER USER把对应账号改成mysql_native_password,快速恢复服务,但要在文档里记录清楚,等后续有窗口期再统一升级客户端。
最后再说一个冷门但实用的技巧:如果你要批量改一批用户的认证插件,不用一个个手敲SQL,可以拼出动态SQL一起执行。先查询生成语句,再复制执行:
sql复制SELECT CONCAT('ALTER USER ''', user, '''@''', host, ''' IDENTIFIED WITH mysql_native_password;')
FROM mysql.user
WHERE plugin = 'caching_sha2_password';
这样会把所有使用新插件的用户都生成一条ALTER语句,确认无误后执行即可。我第一次用这个方法,在一个有四十多个数据库账号的测试环境里,两分钟就把所有账号都改完了,比一个个手敲省太多时间。
