MySQL5.6 + mysql-connector 8.x 报错:CertPathValidatorException Path does not chain with any of the trust anchors 解决方案

6次阅读
没有评论

问题现象

SpringBoot + HikariCP 连接 MySQL5.6,定时任务获取数据库连接时报错,核心堆栈:

Caused by: javax.net.ssl.SSLHandshakeException: (certificate_unknown) java.security.cert.CertPathValidatorException: Path does not chain with any of the trust anchors
Caused by: com.mysql.cj.exceptions.CJCommunicationsException: Communications link failure
The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.

上层异常:

org.springframework.transaction.CannotCreateTransactionException: Could not open JDBC Connection for transaction

特征:Hikari 持续重试创建连接池失败,定时任务反复抛出异常;网络、端口、账号密码本身是通的,问题卡在 SSL/TLS 握手阶段。

很多人第一反应以为是网络不通,实际是证书信任链校验失败。

根因分析

  1. MySQL 5.6 默认的 SSL 证书是 MySQL 内部自生成的自签名证书,不存在合法 CA 信任链;
  2. mysql-connector-java 8.x 驱动默认主动发起 SSL 协商,和老版本驱动行为不一样;
  3. 哪怕你的 MySQL5.6 根本没启用 SSL,驱动依然会尝试走 TLS 握手;握手之后拿到自签证书,JDK 默认信任库(cacerts)里面没有这个根证书,校验直接失败;
  4. 注意区分:MySQL8 是强制 SSL,MySQL5.6 只是支持 SSL,默认不强制开启,坑来自新版驱动的默认行为,不是数据库强制要求加密。

执行 SQL 查看 MySQL5.6 SSL 状态:

show variables like '%ssl%';
  • have_ssl = DISABLED:数据库 SSL 是关闭状态,根本没有启用加密传输,最优方案:直接关闭驱动 SSL 协商
  • have_ssl = YES:数据库开启了 SSL 支持,可以握手

三种解决方案(按推荐优先级排序)

方案一:内网环境首选,直接关闭 SSL 协商(推荐绝大多数场景)

MySQL5.6 内网业务,没有传输加密需求,直接在连接串增加 useSSL=false,驱动不再发起 TLS 握手,从根源规避证书校验问题。

application.properties

spring.datasource.url=jdbc:mysql://127.0.0.1:3306/testdb?useSSL=false&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

application.yml

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/testdb?useSSL=false&serverTimezone=Asia/Shanghai
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver
    hikari:
      maximum-pool-size: 10

补充说明:网上很多文档说 useSSL 参数已经废弃,这个说法主要针对 MySQL8;MySQL5.6 + 8.x 驱动场景,useSSL=false 完全可用,是最稳妥方案。

方案二:需要 SSL 加密传输,临时关闭证书校验(仅应急,公网不推荐)

业务要求走 SSL 加密,但暂时不方便导入证书,可以关闭服务端证书校验,存在中间人劫持风险,仅限临时调试、内网测试,禁止公网生产长期使用。

spring.datasource.url=jdbc:mysql://127.0.0.1:3306/testdb?useSSL=true&verifyServerCertificate=false&serverTimezone=Asia/Shanghai

方案三:正式安全方案,将 MySQL 自签 CA 证书导入 JDK 信任库(公网加密场景)

MySQL5.6 的证书默认存放路径:/var/lib/mysql/,找到 ca.pem 根证书文件。

  1. 导入证书到 JDK 的 cacerts 信任库
keytool -importcert -alias mysql56-ca -file ca.pem -keystore $JAVA_HOME/jre/lib/security/cacerts -storepass changeit

注意:JDK9 + 模块化版本,cacerts 路径有变化;容器化部署建议在 Dockerfile 构建阶段导入证书,不要在运行容器里手动操作。

  1. 连接串正常开启 SSL,不关闭校验
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/testdb?useSSL=true&serverTimezone=Asia/Shanghai

重要避坑清单(MySQL5.6 + 8.x 驱动专属)

  1. 不要随意添加 allowPublicKeyRetrieval=true:该参数是解决 public key retrieval is not allowed 报错,和本次 SSL 证书链报错无关,开启后有安全隐患;
  2. 认证插件:MySQL5.6 使用 mysql_native_password,不支持 caching_sha2_password,这是另外一类密码报错,和 SSL 证书问题区分开;
  3. serverTimezone=Asia/Shanghai 是 8.x 驱动强制要求,MySQL5.6 本身不识别该参数,是驱动侧用来处理时区转换,不能省略;
  4. 先验证基础连通性:在应用所在服务器用原生 mysql 客户端连接数据库,确认 IP、端口、防火墙、账号白名单没问题,排除基础网络问题;
  5. Hikari 池报错特征:反复打印 HikariPool-1 - Starting...,就是持续新建连接失败,连接池无法就绪,所有依赖 DB 的业务(定时任务、接口)全部异常。

排查步骤总结

  1. 登录 MySQL 执行 show variables like '%ssl%',确认数据库 SSL 状态;
  2. have_ssl=DISABLED / 内网无加密需求 → 直接 useSSL=false(首选);
  3. have_ssl=YES,但是不需要严格证书校验 → 临时 verifyServerCertificate=false;
  4. 公网、合规要求 SSL 加密 → 导出 ca.pem,导入 JDK 信任库;
  5. 修改配置后重启应用,观察 Hikari 是否成功初始化连接池。

小结

这个坑本质是新版 JDBC 驱动和老旧 MySQL5.6 的默认行为不匹配。很多人升级驱动后踩坑,优先不要上来就关闭证书校验,内网场景直接关掉 SSL 协商是成本最低、最稳定的解法。

正文完
可以使用微信扫码关注公众号(ID:xzluomor)
post-qrcode
 0
评论(没有评论)
验证码