- 把用户的代码保存为文件
- 编译代码,得到 class 文件
- 执行 Java 代码
- 收集整理输出结果
- 文件清理,释放空间
- 错误处理,提升程序健壮性 区别在于:Java 原生代码沙箱是通过 Runtime.exec 执行命令行操作来执行代码,并通过 Process 对象的流来获取输出结果,不够安全;Docker 代码沙箱是通过创建隔离的Java 容器并目通过 exec 命令在容器内执行Java 代码和获取输出,更加安全。
Docker是一种容器化技术,它允许开发者将应用程序及其所有依赖项打包到一个独立的容器中,包括操作系统库、运行时环境等。这个容器可以在任何支持 Docker 的平台上运行,确保应用程序在不同环境中具有一致的行为。 在本项目中使用 Docker 主要是为了保证代码沙箱服务执行用户代码的安全性,防止影响宿主机。我首先在 Linux 虚拟机内安装了 Docker,然后用 Docker 命令行跑通了一次从拉取镜像、执行容器再到制除容器的完整流程。在代码沙箱项目中,使用 Docker Java 库来操作 Docker,包括 Docker 容器的创建、连接 Docker容器执行命令、获取 Docker 容器的日志和输出、获取 Docker 容器的内存占用等
虽然 Docker 本身提供了一个隔离的代码执行环境,但仍无法做到绝对的安全,所以我通过以下几种方法,进一步提高 Docker 代码沙箱的安全性。 1.超时控制:在向容器发送执行命令时,指定超时参数,超时自动中断 2.资源限制:创建容器实例时,通过 HostConfig 指定分配的最大内存和 CPU 占用网络限制:创建容器实例时,通过 withNetworkDisabled 方法禁用网络3. 权限管理:通过 seccomp 或者Java 安全管理器,限制用户代码允许的操作和调用
由于 Java 原生代码沙箱和 Docker 代码沙箱这两种实现方式的核心业务流程是相同的,且一些过程是完全可以复用的,所以使用模板方法模式。实现步骤如下: 1.先定义了一个抽象的代码沙箱模板类,用一个方法定义了核心流程(保存文件、编译代码、执行代码、收集结果、文件清理、错误处理) 2.在模板类中,把每个环节单独定义为一个方法,可供子类覆写。 3.每种代码沙箱的实现类继承模板类,可以复用模板类的默认环节实现(比如保存文件),也可以重写某一个环节(比 Docker 代码沙箱重写执行逻辑)。 使用模板方法模式后,大幅减少了重复代码,并且让整个系统的流程更加清晰,提升了项目的可扩展性。