Java升级后Runtime.exec执行mysqldump触发IOException权限问题
Java 17执行mysqldump报错权限拒绝(Error 13)排查方案
以下是一段调用mysqldump备份MySQL数据库的Java代码,在Java 8环境下运行正常,但在同一Ubuntu系统的Liberica JDK 17环境中执行失败,抛出IOException(错误码13,权限拒绝)。运行Java进程的用户已拥有mysqldump的执行权限,目标备份目录也归该用户所有。
String osName = System.getProperty("os.name").toLowerCase(); String path = ""; String backupFilePath = backupDir + System.getProperty("file.separator") + "jlawyerdb-dump.sql"; if (osName.contains("win")) { } else if (osName.contains("linux")) { path="/usr/bin/"; } else if (osName.startsWith("mac")) { path = "/usr/local/mysql/bin/"; } log.info("MySQL Dump: " + path + "mysqldump"); String[] cmd = null; if ("".equals(password) || password == null) { cmd = new String[]{ path + "mysqldump", "-P", port, "-u" + user, "-r", backupFilePath, "jlawyerdb" }; } else { cmd = new String[]{ path + "mysqldump", "-P", port, "-u" + user, "-p" + password, "-r", backupFilePath, "jlawyerdb" }; } Runtime shell = Runtime.getRuntime(); Process process = null; File f = new File(backupFilePath); int exitCode = 0; try { if (f.exists()) { f.delete(); } process = shell.exec(cmd); exitCode = process.waitFor(); f.setLastModified(System.currentTimeMillis()); } catch (Exception ex) { log.error(ex); }
测试更新
将代码提取为极简主类测试后,Shell输出如下,对比Java 8和Java 17的运行表现:
root@j-lawyer-box:/usr/local/j-lawyer-server# java -version openjdk version "1.8.0_352" OpenJDK Runtime Environment (build 1.8.0_352-8u352-ga-1~22.04-b08) OpenJDK 64-Bit Server VM (build 25.352-b08, mixed mode) root@j-lawyer-box:/usr/local/j-lawyer-server# java DumpTest MySQL Dump: /usr/bin/mysqldump root@j-lawyer-box:/usr/local/j-lawyer-server# ./jre/bin/java -version openjdk version "17.0.9" 2023-10-17 LTS OpenJDK Runtime Environment (build 17.0.9+11-LTS) OpenJDK 64-Bit Server VM (build 17.0.9+11-LTS, mixed mode, sharing) root@j-lawyer-box:/usr/local/j-lawyer-server# ./jre/bin/java DumpTest MySQL Dump: /usr/bin/mysqldump java.io.IOException: Cannot run program "/usr/bin/mysqldump": error=13, Keine Berechtigung at java.base/java.lang.ProcessBuilder.start(ProcessBuilder.java:1143) at java.base/java.lang.ProcessBuilder.start(ProcessBuilder.java:1073) at java.base/java.lang.Runtime.exec(Runtime.java:594) at java.base/java.lang.Runtime.exec(Runtime.java:453) at DumpTest.main(DumpTest.java:58) Caused by: java.io.IOException: error=13, Keine Berechtigung at java.base/java.lang.ProcessImpl.forkAndExec(Native Method) at java.base/java.lang.ProcessImpl.<init>(ProcessImpl.java:314) at java.base/java.lang.ProcessImpl.start(ProcessImpl.java:244) at java.base/java.lang.ProcessBuilder.start(ProcessBuilder.java:1110) ... 4 more
传递给Runtime.exec的cmd数组参数如下:
/usr/bin/mysqldump -P 3306 -uroot -psecretvalue -r /usr/local/j-lawyer-server/backups/jlawyerdb-dump.sql jlawyerdb
解决建议
- 检查JDK安全限制:Java 17对外部进程执行的权限管控更严格,可尝试添加VM参数
--permit-illegal-access临时测试,或调整JDK安装目录下conf/security/java.security中的相关配置项。 - 替换为ProcessBuilder执行:
Runtime.exec在高版本Java中已不推荐,改用ProcessBuilder可更灵活控制执行环境,示例代码:ProcessBuilder pb = new ProcessBuilder(cmd); // 继承当前进程的环境变量 pb.environment().putAll(System.getenv()); // 指定mysqldump所在目录为工作目录 pb.directory(new File("/usr/bin")); Process process = pb.start(); int exitCode = process.waitFor(); - 排查系统安全模块限制:Ubuntu下的AppArmor或SELinux可能阻止Java 17进程调用
mysqldump,可通过journalctl -xe查看系统审计日志,或临时关闭AppArmor测试(sudo aa-disable /usr/bin/mysqldump)。 - 验证JDK自身权限:检查Liberica JDK安装目录及
java二进制文件的权限,确保运行用户拥有读取和执行权限。 - 优化mysqldump参数传递:避免直接在
-p后拼接密码(部分高版本MySQL会禁用此方式),改用--defaults-extra-file指定包含密码的配置文件,示例:- 创建配置文件
~/.my.cnf,内容:[mysqldump] user=root password=secretvalue port=3306 - 修改cmd数组:
cmd = new String[]{ path + "mysqldump", "--defaults-extra-file=" + System.getProperty("user.home") + "/.my.cnf", "-r", backupFilePath, "jlawyerdb" };
- 创建配置文件
内容的提问来源于stack exchange,提问作者j-dimension
相关产品推荐
相关产品推荐

