ARTICLE DETAIL

资讯详情

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

MySQL建库后Access denied?root@‘%‘和localhost权限没同步是根因

MySQL建库后Access denied?root@‘%‘和localhost权限没同步是根因 简介围绕 MySQL 建库后连接报错的问题这份单页 PDF 为数据库初学者、开发与运维人员整理出一条清晰排错路径。内容从 create database mytest 示例切入逐步拆解 Access denied for user root% to database xxx 的报错原因建库后未授权是核心需要执行 grant all ... with grant option 完成远程访问授权并说明本地访问为何通常不受影响。资源仅1个 PDF 文件、38KB手机或电脑上都能快速翻阅适合作为日常权限问题速查备忘。目前已有32645人学习含常见误区与关键操作要点可帮助遇到同类问题的读者照着修复。1. mysql 创建数据库后 Access denied不是密码错是权限没跟上用 mysql 创建数据库后程序或 Navicat 一连接就报 Access denied for user root% to database xxx这个问题几乎每个刚接触 MySQL 的人都会撞上一次。第一反应通常是改密码、重启服务但都不对症报错码是 1044跟常见的 1045 是两码事1045 是连接被拒1044 是连接成功但没权限。MySQL 里的 root% 和 rootlocalhost 是两个独立账号你在本机建库时用的是后者程序远程连库时匹配的却是前者后者没拿到这个库的授权于是被拒。下面拆一条典型排查路径先定位账号身份再补授权最后用 TCP 强制验证顺带把 auth_socket、host 匹配和 MySQL 8.0 的授权变化这些坑一次填平。适合刚装好 MySQL、建完库却发现应用连不上的人。2. 权限模型的根因root% 与 rootlocalhost 是两个账号2.1 root 不是单独一个用户user 与 host 共同决定身份MySQL 的用户名不是唯一的身份标识完整身份是“用户名 来源主机”。mysql.user 表里每一行都是一个独立的账号主键是 (Host, User)。同一时刻库里可以同时存在 rootlocalhost、root127.0.0.1、root%它们可以有不同的密码、不同的权限、不同的插件甚至可以一个存在一个不存在。你执行 CREATE USER root% 成功不代表 rootlocalhost 会跟着变化这两者完全隔离。很多人不理解为什么同一个 root 会有两套密码就是这个原因。安装 MySQL 时初始化脚本默认创建 rootlocalhosthost 固定为本机。后期为了让远程也能用 root 登录你或教程再创建一个 root%。两个账号之间没有任何继承关系授权也要各做一遍。判断时先查一遍 mysql.user 表看 root 到底有几个 hostmysql.user 中的行常见来源典型登录方式rootlocalhost安装默认本机 socket 或本机 TCProot127.0.0.1手工创建本机 TCP 按 IP 匹配root%手工创建或云环境模板任意主机 TCProot192.168.1.%手工创建内网网段这里最容易产生误解的就是 %。% 是通配符但它在匹配优先级里是最低一档。MySQL 建立连接时会按“精确主机名/IP 网段通配 %”的顺序挑账号。所以即使你建了 root%同一台机器上用 127.0.0.1 连接也可能匹配到更精确的 root127.0.0.1 或反向解析后的 rootlocalhost。给 root% 授权不能保证其他 host 的 root 会话拿到权限。2.2 建库只授权给执行者权限跟着账号走不跟着用户名走CREATE DATABASE 这个动作校验的是全局 CREATE 权限。安装默认给 rootlocalhost 的通常是 ALL所以你本机能建库。但建库之后你在新库里能做什么取决于当前账号的全局权限以及是否存在一条针对这个库的授权记录。MySQL 没有“库属主”这个概念更不会因为你用 root 建了库就把库的权限自动同步给所有 host 的 root 账号。标题场景的典型成因是你在本机用 mysql -uroot -p 通过 socket 登录实际身份是 rootlocalhost执行 CREATE DATABASE xxx 成功随后程序连的是数据库服务器的网络 IPTCP 连接匹配到 root%。这个账号要是没有全局 ALL也没有 xxx 库的库级授权MySQL 就返回 Access denied to database xxx。反直觉的地方在于你明明刚建了库甚至 SHOW DATABASES 里能看到它但就是进不去。这就是权限作用域和账号绑定在一起导致的。排查方向因此分成两条一是 root% 这个账号是否存在二是如果存在它有没有目标库的授权。不要一上来就改密码密码只影响 1045影响不了 1044。2.3 用三条 SQL 把当前状态摸清楚先登录 MySQL执行下面三条语句定位现状-- 1. 列出所有 root 账号重点看 host 和 plugin 两列 SELECT user, host, plugin, account_locked FROM mysql.user WHERE user root; -- 2. 如果 root% 存在单独看它已有的权限 SHOW GRANTS FOR root%; -- 3. 看当前会话实际匹配到的账号而不是你以为的账号 SELECT USER(), CURRENT_USER();第三条最容易忽略。USER() 返回客户端连接时声称的身份比如 root192.168.1.10CURRENT_USER() 返回 MySQL 实际匹配到的账号比如 root192.168.1.% 或 rootlocalhost。两者不一致说明你的连接走了另一个 host 的账号后面所有授权和验证都要以 CURRENT_USER() 为准。SHOW GRANTS FOR root% 如果报 ERROR 1410说明这个账号根本不存在原因在后文第 4 章展开如果返回 GRANT USAGE ON.TO root%说明账号存在但没有任何实际操作权限授权就行如果返回的授权里包含 xxx 库那问题不在权限而在你的连接实际没匹配到这个账号。三条 SQL 跑完方向基本就定了。3. 修复授权三步走CREATE USER、GRANT 与 TCP 验证3.1 授权前先查两件事账号是否存在、现有权限范围授权前不要直接抄 GRANT 语句先确认 root% 处于哪种状态。用一行命令快速检查mysql -uroot -p -e SELECT user,host,plugin FROM mysql.user WHERE userroot; mysql -uroot -p -e SHOW GRANTS FOR root%;-e 参数适合排查时直接执行第一条命令的输出能看出 root 有几个 host、每个 host 用的认证插件。如果输出里没有 root%第二条命令会直接报错这本身就是最有价值的诊断信息。如果输出里只有一条 GRANT USAGE ON.表示 root% 存在但权限是空的需要走 3.2 的授权。这里顺带说明一个匹配细节MySQL 连接匹配账号时host 的优先级是“精确匹配最优先% 兜底”。所以即使 root% 权限正常你的客户端如果是从某个网段连过来而 mysql.user 里恰好有 root192.168.1.% 这个更精确的账号你实际用的还是后者。授权之前把 CURRENT_USER() 的结果当成主线不要凭连接串里的用户名判断。3.2 给已存在的 root% 补授权GRANT ALL 与最小权限账号存在但权限为空直接补一条库级授权-- 调试阶段图省事可以先用 ALL能最快排除权限问题 GRANT ALL PRIVILEGES ON xxx.* TO root%; -- 生产环境建议按需授权别把 DROP 这类权限给应用账号 GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, ALTER, INDEX ON xxx.* TO root%;授权只是把权限记录写入权限表。库名两侧的反引号不是装饰如果库名是 order、select 这类保留字不加反引号语句直接报错。ALL PRIVILEGES 包含 DROP、LOCK TABLES、PROCESS 等权限用在应用账号上风险很大但标题场景是排查 root 的 1044先用 ALL 确认权限链路通了再把权限收窄是常见做法。执行完 GRANT 后再跑一次 SHOW GRANTS FOR root%确认输出里多了 ON xxx.* TO root% 这一行。如果输出没变化多半是你连接时匹配的账号和授权账号不一致回到 2.3 的 CURRENT_USER() 排查。3.3 账号不存在时先 CREATE USERMySQL 8.0 的顺序不能反如果你查 mysql.user 发现没有 root%就直接 GRANT 会撞 1410 错误。MySQL 8.0 已经移除了 GRANT 隐式建用户的行为必须先建账号再授权-- 8.0 正确写法两步不能合并 CREATE USER root% IDENTIFIED BY 这里填一个强密码; GRANT ALL PRIVILEGES ON xxx.* TO root%;MySQL 5.7 及更早的版本允许 GRANT 语句后面跟 IDENTIFIED BY 直接建账号很多旧教程都这么写。到了 8.0 再抄这段服务器只会回你一句 You are not allowed to create a user with GRANT本质是告诉你语法变了。另外8.0 默认的认证插件是 caching_sha2_password如果你的客户端是旧版 JDBC 驱动或某些老版本客户端工具会一直报 1045 密码错误这时需要把插件降级ALTER USER root% IDENTIFIED WITH mysql_native_password BY 这里填一个强密码;这段只在你确认客户端不支持 caching_sha2_password 时才需要执行。新装的客户端一般都能识别 8.0 默认插件没必要为了兼容把密码插件全部降级那样反而削弱安全性。3.4 用 TCP 强制验证修复后必须走一遍真实链路授权改完验证时最忌讳用 socket 登录。mysql 客户端不带 -h 参数时默认走 socket 文件匹配的是 rootlocalhost根本模拟不了程序远程连接的环境。验证要走 TCPmysql -h 数据库服务器IP -P 3306 --protocolTCP -uroot -p--protocolTCP 是关键参数强制客户端走网络协议。如果你在本机验证 127.0.0.1加上这个参数可以避免客户端自作主张退回 socket。登录进去后执行SELECT CURRENT_USER(); USE xxx; SELECT DATABASE();CURRENT_USER() 应该显示 root% 或你实际授权的那个 hostUSE xxx 不再报 Access deniedSELECT DATABASE() 返回 xxx整条链路就通了。注意 USE 这一步才是最严格的验证因为 1044 报错就是在这一步出现的。如果 CURRENT_USER() 显示的是 rootlocalhost说明你的验证连接没走 % 账号即使 USE 成功也只能证明 localhost 账号有权限程序侧的报错依然可能复现这就是为什么必须用 --protocolTCP 和真实 IP 再验一遍。4. 避坑auth_socket、host 匹配与 MySQL 8.0 的授权差异4.1 Ubuntu 的 auth_socket密码正确也报 1045现象在 Ubuntu 或 Debian 上刚装完 MySQL执行 mysql -uroot -p 输入密码报 ERROR 1045 (28000): Access denied for user rootlocalhost (using password: YES)。但执行 sudo mysql 却能直接进。原因Ubuntu 的 MySQL 安装包默认把 rootlocalhost 的认证插件设置成 auth_socket这个插件不校验密码只看发起连接的操作系统用户是不是 root。你在普通用户下带密码连接服务端根本不会进入密码比对流程直接拒绝。这条报错是 1045不是标题里的 1044但两个报错经常被同时搜索很多人从 1044 查到 1045 就绕晕了。解决想让 root 密码登录生效需要先把插件改回密码认证ALTER USER rootlocalhost IDENTIFIED WITH caching_sha2_password BY 这里填新密码;执行完后 mysql -uroot -p 就能正常走密码验证。要注意auth_socket 本身比密码认证更安全如果机器只有你自己用保持 sudo mysql 登录也不是不行但如果你想给程序开远程连接就必须先把 root 或独立账号的密码认证配好。4.2 127.0.0.1 匹配的是 localhost给 root% 授权后本机还是 1044现象给 root% 授了 xxx 库全部权限本机用 mysql -h127.0.0.1 -uroot -p 登录执行 USE xxx 还是报 1044。原因MySQL 解析客户端来源时如果 skip_name_resolve 是 OFF默认值127.0.0.1 会被反向解析成 localhost于是连接匹配到了 rootlocalhost。这个账号没库权限自然被拒。% 在这里根本不参与匹配因为精确匹配优先于通配。解决先确认变量状态再决定走哪条路SHOW VARIABLES LIKE skip_name_resolve;如果变量是 OFF方案有两个。一是把 localhost 也授权GRANT ALL ON xxx.* TO rootlocalhost; 适合本机调试场景。二是在 my.cnf 里设置 skip_name_resolveON 并重启让 127.0.0.1 按 IP 做精确匹配。注意开启后连接串里不能写主机名只能写 IP否则解析不了直接拒绝连接。应用在远程的话用服务器真实 IP 连接就没这个困扰但测试本机链路时很容易踩属于典型的翻车点。4.3 ERROR 1410MySQL 8.0 下 GRANT 不能隐式建用户现象执行 GRANT ALL PRIVILEGES ON xxx.* TO root%; 报 ERROR 1410 (42000): You are not allowed to create a user with GRANT。原因mysql.user 表里没有 root%8.0 把 GRANT 自动建用户的功能移除了。服务器把你的 GRANT 理解成“先建一个用户再授权”而建用户需要 CREATE USER 权限逼着你把两步拆开。5.7 时代的老教程里到处都是直接 GRANT 带 IDENTIFIED BY 的写法照抄就 1410。解决严格按两步走先建用户再授权CREATE USER root% IDENTIFIED BY 这里填强密码; GRANT ALL PRIVILEGES ON xxx.* TO root%;这个报错的变体还会出现在“新创建用户 1045 access denied for user root%”这类搜索词对应的场景里用户建了但忘了授权或授权顺序反了。记住一个原则在 8.0 里CREATE USER 和 GRANT 永远是两条独立的语句顺序不能调换。4.4 云数据库的托管 root命令行授权了也不生效现象在云数据库实例上执行 GRANT ALL ON xxx.* TO root%; 没有报错但应用连接后仍然 1044有些平台是连 GRANT 都直接拒绝报权限不足。原因云数据库平台提供的 root 账号是托管账号全局权限有限库级授权往往要走平台的账号管理入口才生效。命令行里看到的 root 和你自建 MySQL 的 root 不是一回事DBA 能做的事被平台收走了。解决登录云数据库控制台找到“账号管理”或“数据库管理”把目标库关联给对应账号勾选读写权限。部分平台还支持在控制台创建新账号并绑定多个库比命令行更清晰。如果你在命令行里怎么授权都无效果断放弃操作改用控制台这是云环境下的唯一权威路径。排查时可以顺手看一眼 SHOW GRANTS如果 root 的权限里连 ALL ON.都没有基本可以确定是托管账号。4.5 FLUSH PRIVILEGES 不是后悔药别把它当成授权的必填步骤现象每次 GRANT 之后习惯性执行 FLUSH PRIVILEGES;问为什么要执行答不上来只是教程里都这么写。原因GRANT、REVOKE、CREATE USER 本身会更新权限缓存并对后续连接立即生效不需要 FLUSH。FLUSH PRIVILEGES 的真正用途是当你直接用 UPDATE、INSERT、DELETE 修改了 mysql.user、mysql.db 等权限表之后强制服务器重读权限数据。另一个场景是 skip-grant-tables 模式恢复后清掉缓存。解决平时授权后不需要 FLUSH多执行一次没坏处但也没用。真正需要注意的反而是如果你发现授权后必须 FLUSH 才能生效先怀疑是不是有人手动改过权限表或者当前 MySQL 跑在 skip-grant-tables 模式里。把这一行命令从肌肉记忆里删掉有助于建立对权限生效机制的准确判断排查问题时会少走很多弯路。5. 把排查收敛成一段脚本建库授权加验证一条龙排查路径清晰之后我习惯把“建库、建账号、授权、验证”收敛成一个脚本新环境开库时直接复用避免每次手工敲 SQL 漏掉某一步#!/usr/bin/env bash # 用法bash create_db_and_user.sh demo_db app_user AppPass123 set -euo pipefail DB_NAME${1:?请传数据库名} APP_USER${2:?请传应用账号} APP_PASS${3:?请传应用密码} mysql -uroot -p SQL CREATE DATABASE IF NOT EXISTS \$DB_NAME\ DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_0900_ai_ci; CREATE USER IF NOT EXISTS $APP_USER% IDENTIFIED BY $APP_PASS; GRANT SELECT, INSERT, UPDATE, DELETE ON \$DB_NAME\.* TO $APP_USER%; SHOW GRANTS FOR $APP_USER%; SQL echo ---- 验证 TCP 连接 ---- mysql -h 127.0.0.1 --protocolTCP \ -u $APP_USER -p$APP_PASS \ -e USE \$DB_NAME\; SELECT CURRENT_USER(); SELECT DATABASE();脚本里几个细节说明一下CREATE DATABASE IF NOT EXISTS 保证重复执行不报错字符集用 utf8mb4 和 8.0 默认排序规则5.7 环境把排序规则换成 utf8mb4_unicode_ci 即可。CREATE USER IF NOT EXISTS 是 8.0 的幂等写法配合 GRANT 使用比先查账号再判断快很多。GRANT 只给了 SELECT、INSERT、UPDATE、DELETE刻意不给 DROP 和 ALTER这是应用账号的标准最小权限。最后的验证刻意走 127.0.0.1 加 TCP模拟的就是程序通过网络连接数据库的完整链路。脚本里的 root 密码要求交互输入没有明文写在脚本里。生成环境可以把管理密码交给 mysql_config_editor 或密钥服务但脚本结构不用变。实际使用中你可能会想加一个 DROP DATABASE 的清理选项我建议不要做进默认流程DROP 这种操作留在手工执行时更安全。我一般处理完这种 1044会强迫自己走两遍验证一遍看 CURRENT_USER() 确认身份一遍 USE 目标库确认授权。早年图省事只看 SHOW GRANTS结果授权改错了 host白折腾半个下午。把这个习惯固定成脚本之后新建数据库再也没有因为权限问题返工过。希望这几条排查路径能帮你少踩一次一样的坑。本文还有配套的精品资源点击获取
返回列表