ARTICLE DETAIL

资讯详情

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

MySQL 8.0报错1045:ODBC用户访问被拒的原因与修复方法

MySQL 8.0报错1045:ODBC用户访问被拒的原因与修复方法 MySQL 8.0 报ERROR 1045 (28000): Access denied for user ODBClocalhost (using password: NO)很多人在第一次见到它的时候都会懵一下——尤其是那一行ODBClocalhost看起来像是 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 一直存在遇到它说明服务器已经收到了你的连接请求但在校验用户名和密码这一步把你拒了。ODBClocalhost这一节是很多人看糊涂的地方。它表示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 驱动也有类似逻辑。所以你看到的ODBClocalhost并不是 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 数据源或连接串配置不完整这是遇到ODBClocalhost报错的最大概率来源。Windows 的 ODBC 数据源管理器里创建连接时有一栏叫 User很多人填了服务器地址、端口、数据库名唯独把 User 和 Password 留空了测试连接的时候驱动就用 ODBC 兜底用户名MySQL 端直接拒绝。同理如果你在代码里写连接字符串漏掉UID或User字段也会触发同样的行为。我见过一个 .NET 老项目连接串本来是完整的结果有人改配置时手滑删掉了 UID 那一行应用一启动就全线报 1045查了半天才发现是配置文件的问题。这里给一组典型对比方便对照排查连接串写法结果Serverlocalhost;Databasemydb;驱动默认用户 ODBC大概率报 1045Serverlocalhost;Databasemydb;Uidroot;Pwd123456;正常连接DRIVER{MySQL ODBC 8.0 Driver};SERVERlocalhost;PORT3306;DATABASEmydb;USERroot;PASSWORD123456;OPTION3;正常连接2.3 GUI 工具与旧版驱动不兼容这个场景严格来说报的错不完全是ODBClocalhost但如果你用老版本的 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 和ODBClocalhost就叠加出现了。解决思路是升级驱动到 MySQL Connector/ODBC 8.0 以上并显式配置好用户名密码这两个问题一起解决。2.4 Docker 部署 MySQL 时初始化没配好 root 密码用 Docker 跑 MySQL 8.0 的人越来越多这个场景也值得单独拿出来说。docker run创建容器时如果忘了设MYSQL_ROOT_PASSWORD环境变量或者写成MYSQL_ALLOW_EMPTY_PASSWORDyes容器初始化出来的 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 DSNWindows 下打开 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这一列创建用户时写了rootlocalhost然后用mysql -h 192.168.x.x -u root -p去连服务器看到的是来自远程主机 192.168.x.x 的 root而授权表里只有 localhost照样 1045。如果账号确实不存在创建并授权CREATE USER myapplocalhost IDENTIFIED BY YourPass123!; GRANT ALL PRIVILEGES ON mydb.* TO myapplocalhost; FLUSH PRIVILEGES;注意CREATE USER和GRANT操作需要你有管理员权限。如果 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 rootlocalhost IDENTIFIED BY NewPass123!;重置完后把配置文件里加的skip-grant-tables删掉再重启 MySQLsystemctl restart mysqld4.2 方法二--init-file 初始化脚本如果你不想让服务器以无认证状态运行哪怕一分钟可以用--init-file的方式。预先写一个包含重置语句的 SQL 文件然后指定 MySQL 在启动时自动执行它。先创建文件/tmp/mysql-reset.sqlALTER USER rootlocalhost 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 rootlocalhost IDENTIFIED BY NewPass123!;退出后清理临时容器重新启动原容器docker stop mysql8-reset docker rm mysql8-reset docker start mysql8这里要提醒一下以上操作依赖容器数据卷的挂载方式--volumes-from会把原容器所有数据卷继承过来确保 MySQL 的数据目录是同一个。如果你用的是具名卷也可以直接--mount sourcemyvolume,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 密码时不要一上来就设123456或root直接按高强度规则来一步到位。如果你是给老应用重置密码还得多考虑一步认证插件的兼容性。MySQL 8.0 的默认认证插件是 caching_sha2_password如果你的应用用的是旧版客户端或旧版 ODBC 驱动即使密码对了也可能连不上。这时候可以把这个用户的认证方式改成老协议ALTER USER rootlocalhost 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 loadedAccess denied for user xxlocalhost在某些工具里直接显示无法连接或者超时5.2 给老客户端开绿灯的两个方向如果你的业务确实没法升级客户端有两个补救方向第一个方向是把个别用户的认证方式降级为 mysql_native_password前面已经写了 SQL不再重复。这种方式影响面小只改你需要兼容的那个账号。第二个方向是在配置文件中把实例默认认证方式改掉[mysqld] default_authentication_pluginmysql_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.exe64 位应用64 位驱动C:\Windows\System32\odbcad32.exe连接串里同样要写全参数一个标准的 MySQL ODBC 8.0 连接串长这样DRIVER{MySQL ODBC 8.0 Driver};SERVER127.0.0.1;PORT3306;DATABASEmydb;USERroot;PASSWORDYourPass123!;OPTION3;OPTION3这个值不是随便写的它表示启用一些兼容性选项适合常规读写场景。具体每个 bit 位对应什么官方文档里有说明平时不用记知道这个参数存在就够了。6. 常见问题速查表与排查心得6.1 高频报错对照速查表把我和同事们实际处理过的高频报错整理成一张速查表遇到类似问题可以先对号入座报错信息真实含义处理方式Access denied for user ODBClocalhost (using password: NO)客户端没带用户名和密码驱动兜底用 ODBC检查连接串/DSN显式填用户名密码Access denied for user rootlocalhost (using password: YES)用户名对密码不对重置密码或核对密码Access denied for user rootlocalhost (using password: NO)用户名对但没带密码加上-p参数并输入密码Access denied for user root192.168.1.10账号存在但登录来源主机不被允许创建root%或对应网段账号Authentication plugin caching_sha2_password cannot be loaded客户端太旧不认识新认证插件升级驱动或把用户改成 mysql_native_passwordERROR 1819 (HY000): Your password does not satisfy...新密码不符合密码策略按复杂度要求设置密码Cant 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 管理器里测连接是通的应用里却还是报ODBClocalhost。原因通常是应用读的是 32 位 DSN而你配的是 64 位 DSN或者反过来。两个位数各配一遍再测试基本能解决。6.3 最后分享一套排查思路根据我这几年处理各种 MySQL 连接报错的经验遇到 1045 先别慌按这个顺序走基本不会跑偏先确认报错里出现的用户名是不是你期望的那个。如果出现了 ODBC 这种意外用户名几乎可以断定是客户端连接参数的问题优先查连接串、DSN、脚本参数别去动数据库。如果用户名正确再用命令行手工验证一下密码和登录来源能登进去就说明账号没问题问题在应用侧的驱动或网络配置。如果命令行也登不进去才考虑密码重置流程按第 4 节的三种方式选一种适合的。这套思路的核心就是先分清是客户端的问题还是服务端的问题绝大多数 1045 其实都卡在客户端配置上真正需要重置密码的情况反而少。技术排查最忌讳的就是一开始就往服务器上动刀查清楚问题在哪一层往往也就是几分钟的事。
返回列表