从 Dockerized SpringBoot 到外部 MariaDB 的 JDBC 身份验证失败
Posted
技术标签:
【中文标题】从 Dockerized SpringBoot 到外部 MariaDB 的 JDBC 身份验证失败【英文标题】:JDBC Authentication Failure from Dockerized SpringBoot to External MariaDB 【发布时间】:2019-12-29 15:36:18 【问题描述】:当 SpringBoot 服务从 Docker 容器内运行时,我遇到了 SpringBoot Web 服务无法成功验证裸机 MariaDB 10.3.8 数据库的问题。在命令行外部运行时,相同的 SpringBoot 服务 JAR 成功连接到数据库。详情如下:
服务器是在 192.168.99.10 上运行的 Fedora 30 MariaDB 版本 10.3.8 绑定到 0.0.0.0(未 Docker 化) mysql-connector-5.1.41 在 Fedora 30 上运行的 Java OpenJDK 13 Docker 版本 19.03.0-rc3 Docker 镜像 openjdk:13-alpineSpringBoot 应用程序被编码为使用环境变量来定义 MariaDB 数据库实例的位置和凭据。
Java 服务中建立 JDBC 池的类具有额外的工具来确认正在设置环境值并且在实例化 JDBC 池时可用:
@自动连线 公共无效 setDataSource(数据源数据源) // 拉取正在使用的环境变量并记录它们以进行调试 String dbhost = System.getenv("DOCKENV_MYSQL_HOST"); 字符串 dbport = System.getenv("DOCKENV_MYSQL_PORT"); String dbuser = System.getenv("DOCKENV_MYSQL_USERID"); 字符串 dbpw = System.getenv("DOCKENV_MYSQL_PASSWORD"); thisLog.info("DOCKENV_MYSQL_HOST=" + dbhost + " DOCKENV_MYSQL_PORT=" + dbport + " DOCKENV_MYSQL_USERID=" + dbuser + " DOCKENV_MYSQL_PASSWORD=" + dbpw); jdbcTemplate = new JdbcTemplate(dataSource);这是 SpringBoot 服务在 Linux 提示层使用 curl 命令执行和调用时记录的内容。
[mdh@fedora1 ~/gitwork/kuberdepends]$ printenv | grep 码头 DOCKENV_MYSQL_PORT=3306 DOCKENV_MYSQL_PASSWORD=密码错误 DOCKENV_MYSQL_HOST=192.168.99.10 DOCKENV_MYSQL_USERID=dependsapp [mdh@fedora1 ~/gitwork/kuberdepends]$ java -jar 目标/depends.jar 1>/dev/null 2>/dev/null& [1] 7814 [mdh@fedora1 ~/gitwork/kuberdepends]$ [mdh@fedora1 ~/gitwork/kuberdepends]$ curl -H "Content-type: application/json" -X GET http://127.0.0.1:8080//depends/api/projects/34 "project_id":34,"projectstatus_id":0,"clientbusunit_id":2,"clientbusdept_id":2,"factorybusunit_id":11,"factorybusdept_id":23,"projectname":"Ent Portal Unification","shortdescription" :"将企业门户合并到spectrum.net","longdescription":"这里有更长的描述","hascapitalspend":"Y","hasexpensespend":"Y","capitalledger":"","expenseledger":"" ,"clientpriority":1,"deliverypriority":1,"restricttodept":"N","restricttomembers":"N","createdatetime":"2018-05-20 20:51:22.0","updatedatetime":空[mdh@fedora1 ~/gitwork/kuberdepends]$ [mdh@fedora1 ~/gitwork/kuberdepends]$ [mdh@fedora1 ~/gitwork/kuberdepends]$ [mdh@fedora1 ~/gitwork/kuberdepends]$ cat /logs/springboot/dependsLog.txt | grep 依赖应用程序 2019-08-24 13:33:45.077 INFO 7814 --- [main] com.charter.depends.dao.ProjectsDAO:DOCKENV_MYSQL_HOST=192.168.99.10 DOCKENV_MYSQL_PORT=3306 DOCKENV_MYSQL_USERID=dependsapp DOCKENV_MYSQL_PASSWORD=badpassword [mdh@fedora1 ~/gitwork/kuberdepends]$该 JAR 的工作版本被包装为具有以下 Dockerfile 的 Docker 容器:
[mdh@fedora1 ~/gitwork/kuberdepends]$ cat Dockerfile.openjdk13alpine # Docker 文件 -- 用作带有基本 Linux 命令的微型测试映像 # 1) 使用高山图像作为起始切片 # 2) 添加包:iputils、busybox-extras(用于 telnet)、mariadb-client(测试 MariaDB 访问) # 3) 在depends.jar中启动SpringBoot应用程序为java -jar /opt/mdhlabs/depends.jar FROM openjdk:13-alpine 运行 mkdir /opt/mdhlabs 复制 ./target/depends.jar /opt/mdhlabs/depends.jar 工作目录 /opt/mdhlabs 运行 apk 更新 && apk 添加 iputils && apk 添加busybox-extras && apk 添加 mariadb-client CMD ["java", "-jar","/opt/mdhlabs/depends.jar"] [mdh@fedora1 ~/gitwork/kuberdepends]$ [mdh@fedora1 ~/gitwork/kuberdepends]$ docker build -t kuberdepends-alp13 -f Dockerfile.openjdk13alpine 。 (为简洁起见,此处省略了内容...) 成功标记 kuberdepends-alp13:latest [mdh@fedora1 ~/gitwork/kuberdepends]$在停止 Springboot 进程并运行 Dockerized 版本端口后,将内部 8080 从主 Linux 环境映射到外部 7777,这是记录的内容。
[mdh@fedora1 ~/gitwork/kuberdepends]$ docker run --network=host -p 7777:8080 -d -e DOCKENV_MYSQL_HOST='192.168.99.10' -e DOCKENV_MYSQL_PORT='3306' -e DOCKENV_MYSQL_USERID='dependsapp' -e DOCKENV_MYSQL_PASSWORD='badpassword' --name kuberdepends-container kuberdepends-alp13 警告:使用主机网络模式时,已发布的端口将被丢弃 293d36ed488ca076d050f9579bb54979ff9e469a9d4429ad58204f069dbfd358 [mdh@fedora1 ~/gitwork/kuberdepends]$ [mdh@fedora1 ~/gitwork/kuberdepends]$ curl -H "Content-type: application/json" -X GET http://127.0.0.1:7777//depends/api/projects/34 curl: (7) 无法连接到 127.0.0.1 端口 7777: Connection refused [mdh@fedora1 ~/gitwork/kuberdepends]$如果我访问 Docker 容器,对内部 8080 监听端口执行 wget 并检查 SpringBoot 服务生成的日志文件,这里是与尝试实例化 JDBC 池相关的输出。
[mdh@fedora1 ~/gitwork/kuberdepends]$ docker exec -it kuberdepends-container sh /opt/mdhlabs # wget --header "Content-type: application/json" http://127.0.0.1:8080//depends/api/projects/34 连接到 127.0.0.1:8080 (127.0.0.1:8080) wget:服务器返回错误:HTTP/1.1 500 /opt/mdhlabs # /opt/mdhlabs # cat /logs/springboot/dependsLog.txt | grep 依赖应用程序 2019-08-24 18:52:08.956 INFO 1 --- [main] com.charter.depends.dao.ProjectsDAO:DOCKENV_MYSQL_HOST=192.168.99.10 DOCKENV_MYSQL_PORT=3306 DOCKENV_MYSQL_USERID=dependsapp DOCKENV_MYSQL_PASSWORD=badpassword java.sql.SQLException:用户“dependsapp”@“127.0.0.1”的访问被拒绝(使用密码:是) 2019-08-24 18:56:14.796 错误 1 --- [http-nio-8080-exec-1] oaccC[.[.[.[dispatcherServlet] : Servlet.service() 用于 servlet [dispatcherServlet]路径 [/depends/api] 抛出异常 [请求处理失败;嵌套异常是 org.springframework.jdbc.CannotGetJdbcConnectionException: 无法获取 JDBC 连接;嵌套异常是 java.sql.SQLException: Access denied for user 'dependsapp'@'127.0.0.1' (using password: YES)] 根本原因 java.sql.SQLException:用户“dependsapp”@“127.0.0.1”的访问被拒绝(使用密码:是) /opt/mdhlabs #这是让我难过的部分。这不是从容器内部到运行 MariaDB 的外部主机的连接问题,也不是dependsapp/badpassword 的用户ID/密码的身份验证问题。如果我使用安装在 Docker 容器中的 mysql 客户端和凭据,则可以访问数据库。
/opt/mdhlabs # mysql --user=dependsapp --password=badpassword --host=192.168.99.10 依赖 读取表信息以补全表名和列名 您可以使用 -A 关闭此功能以获得更快的启动 欢迎使用 MariaDB 监视器。命令以 ; 结尾或\g。 您的 MariaDB 连接 ID 为 45 服务器版本:10.3.8-MariaDB MariaDB Server 版权所有 (c) 2000、2018、Oracle、MariaDB Corporation Ab 和其他公司。 输入“帮助”;或 '\h' 寻求帮助。键入 '\c' 以清除当前输入语句。 MariaDB [depends]> select project_id, projectname from projects where project_id=34; +------------+------------+ |项目 ID |项目名称 | +------------+------------+ | 34 | Ent 门户统一 | +------------+------------+ 一组中的 1 行(0.000 秒) MariaDB [取决于]>这里是 SpringBoot 应用程序的 Dockerized 版本在尝试连接时生成的异常日志的详细信息。
2019-08-24 18:56:14.605 信息 1 --- [http-nio-8080-exec-1] c.c.depends.services.ProjectController:查询操作 = projectRe 2019-08-24 18:56:14.788 错误 1 --- [http-nio-8080-exec-1] o.a.tomcat.jdbc.pool.ConnectionPool:无法创建初始化 java.sql.SQLException:用户“dependsapp”@“127.0.0.1”的访问被拒绝(使用密码:是) 在 com.mysql.jdbc.SQLError.createSQLException(SQLError.java:964) ~[mysql-connector-java-5.1.41.jar!/:5.1.41] 在 com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3973) ~[mysql-connector-java-5.1.41.jar!/:5.1.41] 在 com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3909) ~[mysql-connector-java-5.1.41.jar!/:5.1.41] 在 com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:873) ~[mysql-connector-java-5.1.41.jar!/:5.1.41] 在 com.mysql.jdbc.MysqlIO.proceedHandshakeWithPluggableAuthentication(MysqlIO.java:1710) ~[mysql-connector-java-5.1. 在 com.mysql.jdbc.MysqlIO.doHandshake(MysqlIO.java:1226) ~[mysql-connector-java-5.1.41.jar!/:5.1.41] 在 com.mysql.jdbc.ConnectionImpl.coreConnect(ConnectionImpl.java:2205) ~[mysql-connector-java-5.1.41.jar!/:5.1.41] 在 com.mysql.jdbc.ConnectionImpl.connectOneTryOnly(ConnectionImpl.java:2236) ~[mysql-connector-java-5.1.41.jar!/:5.1 在 com.mysql.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:2035) ~[mysql-connector-java-5.1.41.jar!/:5.1.41]我假设当连接请求到达外部 MariaDB 数据库时,Docker 容器内的内部 SpringBoot JVM 和 Docker 容器内的内部 mysql 客户端二进制文件似乎都来自相同的源 IP。如果它在 Docker 容器内的 mysql 客户端工作,则在同一个 Docker 容器中的 SpringBoot JVM 内使用时,应该允许相同的用户 ID/密码进行连接。
OpenJDK 12 和 openjdk:12-alpine 也会出现同样的问题,所以我认为这个问题与 Java 中的版本无关。 mysql-connector JAR(版本 5.1.41)是否与 Java 的较新版本或 MariaDB 10.3.x 有问题?
任何帮助将不胜感激。
【问题讨论】:
【参考方案1】:找到根本原因... Pilot 错误。
我的项目树在类路径中有两个单独的 application.properties 文件:
$PROJECT/application.properties $PROJECT/src/main/resources/application.properties
树顶部的版本已用于快速覆盖一次性测试的某些值,但我忘记将该文件与 Docker 映像一起复制到 Docker 映像中
$PROJECT/target/depends.jar
只剩下 src/main/resources/application.properties 的内部版本在容器内执行depends.jar 时使用,留下旧的不正确值。
【讨论】:
以上是关于从 Dockerized SpringBoot 到外部 MariaDB 的 JDBC 身份验证失败的主要内容,如果未能解决你的问题,请参考以下文章
通过 Dockerized Spring Boot 应用程序填充 Dockerized PostgreSQL 数据库
java.net.UnknownHostException 来自 Spring Boot 应用程序的 dockerized mysql
无法从外部 docker 连接到 dockerized redis 实例
Spring Cloud Consul 和 Consul Clients dockerized