mysql ssl用户认证需同时满足四要素:服务端强制require_secure_transport=on、证书链完整、用户语句精准指定subject/issuer、客户端显式提交三证书并启用--ssl-mode=required,缺一则报access denied。

MySQL 基于 SSL 证书的用户身份验证不是“配完证书就能用”,必须同时满足服务端强制、证书链完整、用户语句精准、客户端显式提交四要素,缺一即报 Access denied for user (using password: NO) 这个极具误导性的错误。
服务端必须开启 require_secure_transport=ON 并加载完整证书链
这是双向认证的硬性前提,不是可选项。仅配置 ssl_ca/ssl_cert/ssl_key 只能启用单向 SSL(只验服务端),无法触发客户端证书校验。
-
require_secure_transport=ON强制所有连接走加密通道,否则直接拒绝;不设它,客户端即使带证书也会被降级到非 SSL 连接 - 服务端证书(
server-cert.pem)必须含subjectAltName(如DNS.1 = localhost或IP.1 = 192.168.1.10),否则客户端连接时会报SSL connection error: Certificate verification failed - CA 文件(
ca.pem)必须是 PEM 格式且顺序正确:根 CA 在前,中间 CA(如有)紧随其后;MySQL 不支持多文件拼接,也不能用 DER 格式 - 所有证书文件(
ca.pem、server-cert.pem、server-key.pem)属主必须是mysql用户,权限 ≤600,否则 mysqld 启动时静默失败,日志只显示Failed to set up SSL
创建用户时不能只写 REQUIRE X509,必须锁定 SUBJECT 或 ISSUER
REQUIRE X509 仅检查证书格式和签名有效性,不校验是谁签发、谁持有——私钥一旦泄露,攻击者可用同 CA 下任意合法证书登录。
- 推荐写法:
CREATE USER 'api'@'%' REQUIRE SUBJECT '/CN=api-client/OU=Service/O=MyOrg/C=CN';注意斜杠与等号间**不能有空格**,大小写和字段顺序必须与openssl x509 -in client-cert.pem -subject -noout输出完全一致 - 若需更严格控制,叠加
AND ISSUER '/CN=MyMySQLCA',确保只接受指定 CA 签发的证书 - 执行后务必运行
FLUSH PRIVILEGES,否则新策略不生效 - 切勿对
root用户配置REQUIRE X509,容易因证书问题彻底锁死管理入口
客户端连接必须显式传入三证书 + --ssl-mode=REQUIRED
MySQL 默认不启用 SSL,即使服务端强制,客户端不主动声明,连接仍会 fallback 到非加密通道,导致认证流程跳过证书校验阶段。
- 命令行示例:
mysql --ssl-mode=REQUIRED --ssl-ca=ca.pem --ssl-cert=client-cert.pem --ssl-key=client-key.pem -u api - Python PyMySQL 需传入字典:
ssl={'ca': 'ca.pem', 'cert': 'client-cert.pem', 'key': 'client-key.pem'},缺任一字段都会失败 -
--ssl-mode=VERIFY_IDENTITY更安全,但要求服务端证书的 SAN 匹配连接地址(如用 IP 连接却只写了 DNS 名),内网调试时容易误踩,建议先用REQUIRED - Java JDBC 连接字符串中,
useSSL=true已废弃,必须用sslMode=REQUIRED,并配合trustCertificateKeyStoreUrl指向 JKS 密钥库(需用keytool将ca.pem导入)
验证证书链是否完整是排查失败的第一步
绝大多数 “Access denied” 实际源于 OpenSSL 层校验失败,而非 MySQL 权限逻辑问题。服务端日志往往不报明错,必须手动验证。
- 在客户端机器上运行:
openssl verify -CAfile ca.pem client-cert.pem;返回OK才算通过基础链路验证 - 若失败,常见原因是:CA 文件缺失中间证书、
client-cert.pem里混入了私钥、或ca.pem权限不对(OpenSSL 要求只读) - 服务端启动后,可查状态:
SHOW VARIABLES LIKE '%ssl%';确认have_ssl为YES,且ssl_ca路径正确;再执行STATUS查看当前连接是否启用了 SSL - 不要依赖客户端错误提示判断问题——
using password: NO是伪装,真实原因永远藏在服务端错误日志或 OpenSSL 验证结果里
真正卡住人的地方,从来不是生成证书那几步命令,而是证书链顺序、SUBJECT 字符串空格、文件权限、以及客户端是否真的把三个 PEM 文件都送到了 OpenSSL 握手环节——这些细节不逐项验证,光改 SQL 或配置文件毫无意义。











