Spring Boot服务端如何调用运行同仓库脚本并解决文件找不到问题
Spring Boot调用项目内Shell脚本找不到文件的解决方案
问题核心原因
你当前直接将前端传入的脚本名传入ProcessBuilder启动进程,进程默认会从JVM启动时的工作目录查找对应文件,没有指定脚本的实际存放路径,也没有做路径合法性校验,因此触发文件找不到报错。
分步修复方案
1. 统一脚本存放规则
把所有可执行脚本统一放到项目src/main/resources/scripts目录下,同时在application.properties/application.yml中配置脚本路径,实现本地和AWS环境适配:
# 本地调试配置 app.scripts.path=src/main/resources/scripts # AWS环境部署时改成对应服务器路径,比如EC2上的/opt/your_app/scripts # app.scripts.path=/opt/your_app/scripts
2. 新增安全校验逻辑
必须做路径合法性校验,避免路径穿越漏洞,比如用户传入../rm -rf /*这类恶意脚本名会直接威胁服务安全,建议只允许执行白名单内的脚本,同时校验脚本路径是否落在允许的目录范围内。
3. 修复控制器代码
import org.springframework.beans.factory.annotation.Value; import org.springframework.util.FileCopyUtils; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.Files; @RestController public class ScriptController { @Value("${app.scripts.path}") private String scriptsBasePath; // 白名单:只允许执行列表内的脚本,按需修改 private static final List<String> ALLOWED_SCRIPTS = List.of("test1.sh", "backup.sh", "sync_data.sh"); @PostMapping public ResponseEntity<SampleRest> runScript(@Valid @RequestBody ScriptsDetailsRequestModel scriptDetails) throws IOException { String scriptName = scriptDetails.getScriptName(); // 1. 校验脚本是否在白名单内 if (!ALLOWED_SCRIPTS.contains(scriptName)) { return new ResponseEntity<>(HttpStatus.FORBIDDEN); } // 2. 拼接脚本完整路径,校验路径合法性 Path scriptPath = Paths.get(scriptsBasePath, scriptName).normalize(); if (!scriptPath.startsWith(Paths.get(scriptsBasePath).normalize())) { // 路径穿越攻击,直接拒绝 return new ResponseEntity<>(HttpStatus.FORBIDDEN); } if (!Files.exists(scriptPath)) { return new ResponseEntity<>(HttpStatus.NOT_FOUND); } // 3. 构造执行命令,指定bash解释器,避免执行权限问题 String[] cmd = new String[]{"/bin/bash", scriptPath.toAbsolutePath().toString()}; ProcessBuilder pb = new ProcessBuilder(cmd); // 可选:配置工作目录、环境变量 pb.directory(Paths.get(scriptsBasePath).toFile()); // 4. 启动进程,必须读取进程输出流和错误流,否则进程会卡住 Process p = pb.start(); // 异步读取输出,避免阻塞 new Thread(() -> { try { // 读取标准输出 FileCopyUtils.copy(p.getInputStream(), System.out); // 读取错误输出 FileCopyUtils.copy(p.getErrorStream(), System.err); } catch (IOException e) { e.printStackTrace(); } }).start(); // 可选:等待脚本执行完成,或者直接返回 // int exitCode = p.waitFor(); SampleRest returnValue = new SampleRest(); returnValue.setMsg("脚本执行触发成功"); return new ResponseEntity<>(returnValue, HttpStatus.OK); } }
环境适配注意事项
本地调试
- 给脚本目录下的所有sh文件加执行权限:
chmod +x src/main/resources/scripts/*.sh - 确认IDEA启动项目的工作目录是项目根目录,和配置的
app.scripts.path匹配
AWS环境适配
- 如果部署到EC2/ECS:部署时将scripts目录同步到配置的
app.scripts.path对应路径,同时保留脚本的执行权限 - 如果部署到Lambda:Lambda只允许读写
/tmp目录,启动时需要把类路径下的脚本复制到/tmp目录再执行,修改脚本基础路径配置为/tmp/scripts即可
内容的提问来源于stack exchange,提问作者Helpthisamatuerprogrammer
相关产品推荐
相关产品推荐

