MySQL 8.0报错1045:ODBC用户访问被拒的原因与修复方法
2026/9/18 12:04:38 网站建设 项目流程

MySQL 8.0 报ERROR 1045 (28000): Access denied for user 'ODBC'@'localhost' (using password: NO),很多人在第一次见到它的时候都会懵一下——尤其是那一行'ODBC'@'localhost',看起来像是 MySQL 里存在一个叫 ODBC 的用户,于是下意识地去翻用户表、去创建账号,结果折腾半天毫无进展。我最早踩这个坑是在 Windows 服务器上给一个老系统配 ODBC 数据源,应用一启动就报这个错,当时我也以为是权限问题,找了一圈才发现问题根本不在 MySQL 这一侧,而是客户端在连接时压根没把用户名传过来。

这篇文章我打算把这个报错彻底讲透。从错误信息的读法、ODBC 用户从哪来,到不同场景下的修复方案,再到 MySQL 8.0 认证插件引发的一系列连锁问题,最后附上我实际排查中积累的速查表和避坑心得。无论你是刚装完 MySQL 8.0 的新手,还是被老应用 ODBC 连接折腾的运维,照着文章顺序走一遍,基本都能把问题定位出来。

1. 拆解报错:ERROR 1045 到底在说什么

1.1 错误信息里的三个关键片段

先把这个报错拆开看。ERROR 1045 (28000)是 MySQL 的客户端错误码,含义是身份认证失败,拿数据库术语说就是 Access denied。这个错误码从 MySQL 5.x 到 8.x 一直存在,遇到它说明服务器已经收到了你的连接请求,但在校验用户名和密码这一步把你拒了。

'ODBC'@'localhost'这一节是很多人看糊涂的地方。它表示MySQL 服务器在连接握手中收到的客户端用户名是 ODBC,来源主机是 localhost。这里有个很重要的点:报错里出现的用户名,是客户端主动告诉 MySQL 的,不是 MySQL 自己猜的。你的客户端工具、ODBC 驱动或者中间层组件在建立连接时,把用户名字段填成了 ODBC,服务器只是把这个名字拿去和权限表比对,比对不上就拒绝。

最后的(using password: NO)说的是这次连接请求没有附带任何密码。结合起来理解,整条报错翻译成人话就是:有个自称是 ODBC 的客户端,从本机发来连接请求,没带密码,MySQL 的权限表里找不到能匹配的账号,所以拒绝访问。

1.2 为什么会有 ODBC 这个用户名

ODBC 是 Open Database Connectivity 的缩写,Windows 上做数据库连接经常接触这套标准。问题就出在它身上:很长一段时间里,各大数据库的 ODBC 驱动都有一个兜底行为——如果连接字符串或数据源(DSN)里没有显式写用户名,驱动就会默认用 ODBC 作为用户名去请求连接。这个行为不是 MySQL 特有的,SQL Server、Oracle 的老 ODBC 驱动也有类似逻辑。

所以你看到的'ODBC'@'localhost'并不是 MySQL 的某个内置账号,纯粹是驱动在缺省情况下的默认用户名。知道这一点之后,这个报错的解决方向就很明确了:不要想着去 MySQL 里建一个叫 ODBC 的用户,而是要让客户端把正确的用户名传过去。当然,如果应用代码写死了用 ODBC 用户连接,那你确实可以创建一个 ODBC 账号来救急,但那是补丁式修法,我不建议,后面细说。

2. 定位原因:这类报错最常见的 4 个触发场景

2.1 命令行客户端没有指定用户名

最简单也最容易犯的一种:在 Windows 的命令行里直接敲mysql或者mysql -h localhost,没带-u参数。MySQL 客户端在缺少用户名时,会去读操作系统的当前用户名,但在某些 Windows 环境变量缺失、或者是在批处理脚本里调用时,这个值可能拿不到,最后就落到了 ODBC 上。

我在排查用户报障时见过不少这样的例子:应用服务器上写了个计划任务调用 MySQL 做数据导出,脚本里写的是mysql -h localhost < export.sql,完全没带用户信息,任务一跑就是 1045。这里的教训是,命令行连接必须显式写-u 用户名 -p,哪怕你本机只有一个 root,也别省这两个参数。

2.2 ODBC 数据源或连接串配置不完整

这是遇到'ODBC'@'localhost'报错的最大概率来源。Windows 的 ODBC 数据源管理器里创建连接时,有一栏叫 User,很多人填了服务器地址、端口、数据库名,唯独把 User 和 Password 留空了,测试连接的时候驱动就用 ODBC 兜底用户名,MySQL 端直接拒绝。

同理,如果你在代码里写连接字符串,漏掉UIDUser字段,也会触发同样的行为。我见过一个 .NET 老项目,连接串本来是完整的,结果有人改配置时手滑删掉了 UID 那一行,应用一启动就全线报 1045,查了半天才发现是配置文件的问题。

这里给一组典型对比,方便对照排查:

连接串写法结果
Server=localhost;Database=mydb;驱动默认用户 ODBC,大概率报 1045
Server=localhost;Database=mydb;Uid=root;Pwd=123456;正常连接
DRIVER={MySQL ODBC 8.0 Driver};SERVER=localhost;PORT=3306;DATABASE=mydb;USER=root;PASSWORD=123456;OPTION=3;正常连接

2.3 GUI 工具与旧版驱动不兼容

这个场景严格来说报的错不完全是'ODBC'@'localhost',但如果你用老版本的 MySQL ODBC 驱动(5.1、5.2、5.3 系列)去连 MySQL 8.0,很可能先碰到 1045,或者报Authentication plugin 'caching_sha2_password' cannot be loaded。MySQL 8.0 把默认认证插件换成了 caching_sha2_password,老驱动不认识这个插件,握手阶段就可能失败。

如果这时驱动在用户名缺失的情况下再用 ODBC 兜底,那 1045 和'ODBC'@'localhost'就叠加出现了。解决思路是升级驱动到 MySQL Connector/ODBC 8.0 以上,并显式配置好用户名密码,这两个问题一起解决。

2.4 Docker 部署 MySQL 时初始化没配好 root 密码

用 Docker 跑 MySQL 8.0 的人越来越多,这个场景也值得单独拿出来说。docker run创建容器时如果忘了设MYSQL_ROOT_PASSWORD环境变量,或者写成MYSQL_ALLOW_EMPTY_PASSWORD=yes,容器初始化出来的 root 账号密码状态就和你期望的不一样。后续在宿主机上配置 ODBC 连接时,如果连接串里用户名密码都不对,驱动兜底成 ODBC 用户名,报错当然逃不开 1045。

另外一个很隐蔽的坑:MYSQL_ROOT_PASSWORD只在数据目录首次初始化时生效。如果你之前已经跑过一个容器,后来删掉容器但保留了数据卷,再重新docker run时即使写了新的 root 密码也不起作用,因为密码已经写在卷里的授权表中了。

3. 快速修复:先别重置密码,试试这几步

3.1 命令行连接的正确姿势

先确认 MySQL 服务本身是正常的,然后用最标准的方式手工验证登录:

mysql -h localhost -P 3306 -u root -p

输入密码后如果能进入,说明服务端没问题,问题出在客户端的连接配置上。这时再回头检查你的应用连接串、ODBC DSN 或者脚本,把用户名密码补全即可。

在 Linux 上还要注意一点:mysql -u root不带-p时,客户端依然会尝试无密码登录;如果 root 在auth_socket插件下,只有系统 root 用户通过本地 socket 能免密登录,普通场景下照样会被 1045 拒绝。

3.2 修复 Windows ODBC DSN

Windows 下打开 ODBC 数据源管理器的方式是:控制面板 → 管理工具 → ODBC 数据源(64位/32位根据应用来选),或者直接在运行框输odbcad32.exe。进去后找到你的 DSN 配置,重点检查这几个字段:

  • User:必须显式填,不能留空
  • Password:填对应账号的密码
  • 如果填了 Database,要确保账号对这个库有权限

填完点 Test 测试,能通就说明问题解决。这里有个经常被忽略的细节:64 位系统上 32 位应用必须用 32 位的 ODBC 管理器配置 DSN,否则应用侧看到的配置和系统侧不一致,测试时明明通了,应用一跑还是老报错。

3.3 验证 MySQL 用户与授权信息

如果连接配置看着没问题,那就要确认一下 MySQL 里到底有没有你要用的账号,以及它允许从哪些主机登录:

SELECT user, host, plugin FROM mysql.user;

这条命令把当前实例的所有账号列出来。重点看两件事:一是你要连接的账号是否存在,二是它允许的主机范围。很多新手会忽略host这一列,创建用户时写了'root'@'localhost',然后用mysql -h 192.168.x.x -u root -p去连,服务器看到的是来自远程主机 192.168.x.x 的 root,而授权表里只有 localhost,照样 1045。

如果账号确实不存在,创建并授权:

CREATE USER 'myapp'@'localhost' IDENTIFIED BY 'YourPass123!'; GRANT ALL PRIVILEGES ON mydb.* TO 'myapp'@'localhost'; FLUSH PRIVILEGES;

注意:CREATE USERGRANT操作需要你有管理员权限。如果 root 本身也登不进去,那就直接跳到下一节的密码重置方案。

4. 终极兜底:root 也进不去时的密码重置方案

4.1 方法一:--skip-grant-tables 模式

这套方法的核心思路是让 MySQL 启动时跳过授权表检查,从而在不验证密码的情况下进入系统,然后重置密码。Linux 系统上操作如下:

先把 MySQL 停掉:

systemctl stop mysqld

然后用跳过授权表的方式启动:

mysqld --skip-grant-tables --skip-networking &

加上--skip-networking是为了防止其他主机趁这个间隙连进来,这个安全意识很重要,因为跳过授权表意味着服务器没有任何认证保护。如果是在 RHEL/CentOS 系上,mysqld 的启动参数可能受 systemd 限制,更稳妥的做法是修改/etc/my.cnf,在[mysqld]段下加一行skip-grant-tables,然后正常启动服务:

systemctl start mysqld

接下来直接无密码进入:

mysql -uroot

进入后先执行FLUSH PRIVILEGES,这一步在 MySQL 8.0 里是必须的。跳过授权表启动时,服务器不会加载授权表到内存,如果不先 FLUSH,直接执行ALTER USER会报ERROR 1290 (HY000): The MySQL server is running with the --skip-grant-tables option so it cannot execute this statement。这是我实际踩过的坑,网上很多老教程没提这点,照着做就会卡在这一步。

FLUSH PRIVILEGES; ALTER USER 'root'@'localhost' IDENTIFIED BY 'NewPass123!';

重置完后,把配置文件里加的skip-grant-tables删掉,再重启 MySQL:

systemctl restart mysqld

4.2 方法二:--init-file 初始化脚本

如果你不想让服务器以无认证状态运行哪怕一分钟,可以用--init-file的方式。预先写一个包含重置语句的 SQL 文件,然后指定 MySQL 在启动时自动执行它。

先创建文件/tmp/mysql-reset.sql

ALTER USER 'root'@'localhost' IDENTIFIED BY 'NewPass123!';

然后修改配置文件或在启动命令中指定:

mysqld --init-file=/tmp/mysql-reset.sql &

MySQL 启动时会执行文件里的 SQL,执行完再正常加载权限系统。注意一点:--init-file文件里的 SQL 执行失败不会阻止 MySQL 启动,所以重置完之后最好验证一下新密码能不能登录。如果用的是ALTER USER语法,MySQL 8.0 也要求此时服务器已经加载授权表,所以不需要像 skip-grant-tables 那样先 FLUSH。

执行成功后删掉这个 SQL 文件,正常重启服务即可。

4.3 方法三:Docker 容器内的处理

Docker 场景下如果 root 密码不对,处理思路和普通环境略有差别。最推荐的做法是利用数据卷另起一个临时容器来重置。

假设原容器叫mysql8,先停止它:

docker stop mysql8

然后用同样的数据卷启动一个临时容器,并让 mysqld 以跳过授权表方式运行:

docker run -d --name mysql8-reset --volumes-from mysql8 mysql:8.0 mysqld --skip-grant-tables --skip-networking

等待几秒让它完成初始化,进入这个临时容器:

docker exec -it mysql8-reset mysql -uroot

进入 MySQL 后执行:

FLUSH PRIVILEGES; ALTER USER 'root'@'localhost' IDENTIFIED BY 'NewPass123!';

退出后清理临时容器,重新启动原容器:

docker stop mysql8-reset docker rm mysql8-reset docker start mysql8

这里要提醒一下:以上操作依赖容器数据卷的挂载方式,--volumes-from会把原容器所有数据卷继承过来,确保 MySQL 的数据目录是同一个。如果你用的是具名卷,也可以直接--mount source=myvolume,target=/var/lib/mysql指定挂载,效果一样。

另有一种比较省事的思路是直接用docker exec -it mysql8 mysql -uroot去碰碰运气。如果当初创建容器时设置了MYSQL_ROOT_PASSWORD但你忘了密码,并且数据卷是初次初始化,root 密码就是你设置的那个值;只有在你压根没设置过的情况下,才需要走上面这套重置流程。

4.4 MySQL 8.0 密码策略与认证插件的坑

重置密码时,如果你设置的密码太简单,MySQL 8.0 会直接给你颜色看:

ERROR 1819 (HY000): Your password does not satisfy the current policy requirements

这是 validate_password 组件在把关。默认的密码策略要求密码至少 8 位,而且要包含大小写字母、数字和特殊字符。所以重置 root 密码时不要一上来就设123456root,直接按高强度规则来,一步到位。

如果你是给老应用重置密码,还得多考虑一步认证插件的兼容性。MySQL 8.0 的默认认证插件是 caching_sha2_password,如果你的应用用的是旧版客户端或旧版 ODBC 驱动,即使密码对了也可能连不上。这时候可以把这个用户的认证方式改成老协议:

ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'NewPass123!';

改动前先确认这个实例上运行的业务是否有老客户端依赖。如果只是给自己用,建议直接用 MySQL 8.0 官方最新驱动,没必要为了兼容老组件而降低安全性。

5. MySQL 8.0 专属坑位:认证插件与客户端兼容性

5.1 caching_sha2_password 和 mysql_native_password 的区别

MySQL 5.7 及更早版本的默认认证插件是 mysql_native_password,传输密码时用的是 SHA1 算法,后来被发现存在安全隐患。MySQL 8.0 换成了 caching_sha2_password,基于 SHA256,安全性提升明显,同时对服务端性能也更友好——它在首次认证后会缓存认证结果,后续连接走缓存,不用每次都做全套密码哈希运算。

对终端用户来说,最大的感知差异是老的客户端和驱动认不出新插件。MySQL 官方自带的 8.0 客户端没问题,但如果你用的是很老的 Navicat 版本、旧版驱动,或者系统自带的很老的 ODBC 驱动,连接时就会失败,报错信息五花八门,常见的有:

  • Authentication plugin 'caching_sha2_password' cannot be loaded
  • Access denied for user 'xx'@'localhost'
  • 在某些工具里直接显示无法连接或者超时

5.2 给老客户端开绿灯的两个方向

如果你的业务确实没法升级客户端,有两个补救方向:

第一个方向是把个别用户的认证方式降级为 mysql_native_password,前面已经写了 SQL,不再重复。这种方式影响面小,只改你需要兼容的那个账号。

第二个方向是在配置文件中把实例默认认证方式改掉:

[mysqld] default_authentication_plugin=mysql_native_password

改完重启后,新建的用户会默认使用老认证方式,但这不会影响已存在的用户。注意这个参数在 MySQL 8.0 中已被标记为废弃,不建议长期依赖,只适合过渡期使用。新项目的正确做法是用 8.0 的驱动和客户端,保持默认的 caching_sha2_password。

5.3 ODBC 驱动的选型建议

回到标题里的 ODBC,如果你是用 ODBC 方式连 MySQL 8.0,驱动版本必须在 8.0 以上。下载时注意区分 32 位和 64 位,这个安装细节直接影响连接结果。很多人装了 64 位驱动,但应用是 32 位的,ODBC 管理器里死活看不到驱动,或者连接报错。遇到这种情况,装一个对应位数版本,问题立刻消失。

驱动的架构选择可以参考下表:

应用位数驱动位数ODBC 管理器入口
32 位应用32 位驱动C:\Windows\SysWOW64\odbcad32.exe
64 位应用64 位驱动C:\Windows\System32\odbcad32.exe

连接串里同样要写全参数,一个标准的 MySQL ODBC 8.0 连接串长这样:

DRIVER={MySQL ODBC 8.0 Driver};SERVER=127.0.0.1;PORT=3306;DATABASE=mydb;USER=root;PASSWORD=YourPass123!;OPTION=3;

OPTION=3这个值不是随便写的,它表示启用一些兼容性选项,适合常规读写场景。具体每个 bit 位对应什么,官方文档里有说明,平时不用记,知道这个参数存在就够了。

6. 常见问题速查表与排查心得

6.1 高频报错对照速查表

把我和同事们实际处理过的高频报错整理成一张速查表,遇到类似问题可以先对号入座:

报错信息真实含义处理方式
Access denied for user 'ODBC'@'localhost' (using password: NO)客户端没带用户名和密码,驱动兜底用 ODBC检查连接串/DSN,显式填用户名密码
Access denied for user 'root'@'localhost' (using password: YES)用户名对,密码不对重置密码或核对密码
Access denied for user 'root'@'localhost' (using password: NO)用户名对,但没带密码加上-p参数并输入密码
Access denied for user 'root'@'192.168.1.10'账号存在但登录来源主机不被允许创建'root'@'%'或对应网段账号
Authentication plugin 'caching_sha2_password' cannot be loaded客户端太旧,不认识新认证插件升级驱动,或把用户改成 mysql_native_password
ERROR 1819 (HY000): Your password does not satisfy...新密码不符合密码策略按复杂度要求设置密码
Can't connect to MySQL server on 'localhost' (10061)MySQL 服务没启动或端口没监听检查服务状态和防火墙

6.2 我踩过的几个坑

第一个坑是 skip-grant-tables 模式下直接执行 ALTER USER,报 1290。这个问题在 MySQL 8.0 里特别容易遇到,因为 8.0 对授权表操作的检测更严格了。解决方案前面已经写了,先FLUSH PRIVILEGES再执行 ALTER,顺序不能反。

第二个坑是 Docker 场景下重置完密码后忘了清理临时容器,结果两个容器同时在跑,端口和资源都冲突。我建议重置流程走完后顺手把临时容器删掉,别留着。

第三个坑是密码重置完成后没有及时移除skip-grant-tables配置。这个比较危险,服务器处于无认证状态,任何人只要知道端口就能连进来。每次操作完,记得回头检查配置文件,确认没有残留的跳权选项。

第四个坑不太起眼,但很折磨人:Windows 下 ODBC 管理器里测连接是通的,应用里却还是报'ODBC'@'localhost'。原因通常是应用读的是 32 位 DSN,而你配的是 64 位 DSN,或者反过来。两个位数各配一遍,再测试基本能解决。

6.3 最后分享一套排查思路

根据我这几年处理各种 MySQL 连接报错的经验,遇到 1045 先别慌,按这个顺序走基本不会跑偏:

先确认报错里出现的用户名是不是你期望的那个。如果出现了 ODBC 这种意外用户名,几乎可以断定是客户端连接参数的问题,优先查连接串、DSN、脚本参数,别去动数据库。如果用户名正确,再用命令行手工验证一下密码和登录来源,能登进去就说明账号没问题,问题在应用侧的驱动或网络配置。如果命令行也登不进去,才考虑密码重置流程,按第 4 节的三种方式选一种适合的。

这套思路的核心就是先分清是客户端的问题还是服务端的问题,绝大多数 1045 其实都卡在客户端配置上,真正需要重置密码的情况反而少。技术排查最忌讳的就是一开始就往服务器上动刀,查清楚问题在哪一层,往往也就是几分钟的事。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询