java 代码沙箱的实现篇
在线判题系统的核心功能模块
前言
代码沙箱其实就是java的编译执行过程,我们只需要完成这些过程即可,需要注意的是,防止写入的代码带病毒,乱写入,死循环之类的问题,我们可以用黑白名单来解决,比如把File加入黑名单
代码沙箱实现逻辑
1、编译前的准备
1.传入的参数
public class ExecuteCodeRequest {
private List<String> inputList;
private String code;
private String language;
}
- 题目需要输入的参数集合
- 代码内容
- 使用编译语言
2.传出结果参数类
public class ExecuteCodeResponse {
private List<String> outputList;
/**
* 接口信息
*/
private String message;
/**
* 执行状态
*/
private Integer status;
/**
* 判题信息
*/
private JudgeInfo judgeInfo;
}
- 我们应该都知道,每一个需要判题程序可以用很多不同的类名,所以我们固定类名,这样我们的java代码在的时候执行的时候就不会那么的麻烦,只需要写好一个字符串为javac (文件路径),就能够生成一个Main的class类,然后执行即可。
private static final String GLOBAL_JAVA_CLASS_NAME = "Main.java";
2、存放代码及编译代码
将用户代码保存为文件
/**
* 1. 把用户的代码保存为文件
* @param code 用户代码
* @return
*/
public File saveCodeToFile(String code) {
String userDir = System.getProperty("user.dir");
String globalCodePathName = userDir + File.separator + GLOBAL_CODE_DIR_NAME;
// 判断全局代码目录是否存在,没有则新建
if (!FileUtil.exist(globalCodePathName)) {
FileUtil.mkdir(globalCodePathName);
}
// 把用户的代码隔离存放
String userCodeParentPath = globalCodePathName + File.separator + UUID.randomUUID();
String userCodePath = userCodeParentPath + File.separator + GLOBAL_JAVA_CLASS_NAME;
File userCodeFile = FileUtil.writeString(code, userCodePath, StandardCharsets.UTF_8);
return userCodeFile;
}
我们固定一个位置,来存放我们的代码,后续我们执行完成后将其删除
Process类
Process 类通常用于表示和管理操作系统中的进程。在多任务操作系统中,进程是执行中的程序实例,它包含程序计数器、寄存器、堆栈和程序执行的内存区域等。不同的编程语言或框架可能有不同的 Process 类实现,但通常它们都提供了一些共同的功能。
- 方法
-
Start()
描述:启动进程。
参数:可能包括命令行参数、工作目录等。
返回值:通常无返回值,但可能抛出异常。 -
Stop() 或 Kill()
描述:停止或终止进程。
参数:可能包括终止信号等。
返回值:通常无返回值,但可能抛出异常。 -
WaitForExit()
描述:等待进程退出。
参数:可能包括超时时间。
返回值:通常返回进程的退出代码。 -
Read() 或 GetOutput()
描述:读取进程的输出。
参数:可能包括读取的缓冲区大小等。
返回值:进程的输出内容。 -
Write() 或 Input()
描述:向进程发送输入。
参数:要发送的输入内容。
返回值:通常无返回值,但可能抛出异常。
编译代码
/**
* 2、编译代码
* @param userCodeFile
* @return
*/
public ExecuteMessage compileFile(File userCodeFile) {
String compileCmd = String.format("javac -encoding utf-8 %s", userCodeFile.getAbsolutePath());
try {
Process compileProcess = Runtime.getRuntime().exec(compileCmd);
ExecuteMessage executeMessage = ProcessUtils.runProcessAndGetMessage(compileProcess, "编译");
log.info("{},{}",executeMessage.getExitValue(),executeMessage.getErrorMessage());
if (exec