沙箱運行代碼
現在有一個需求是:我們需要把別人的代碼加載到我們代碼裏面來運行
如何把別人代碼拿過來呢?
- 可以利用git把代碼下載下來
- 利用maven進行打包編譯(.java編譯成.class文件)
- 加載.class文件形成object對象
- 運行我們想要的函數
Git git = Git.cloneRepository().setURI(gitUrl).setDirectory(localRepoFile).call();
InvocationRequest request = new DefaultInvocationRequest();
request.setPomFile(new File(localRepoPath + "/pom.xml"));
request.setGoals(Collections.singletonList("package"));
request.setMavenOpts("-Dmaven.test.skip=true");
Invoker invoker = new DefaultInvoker();
invoker.setMavenHome(new File(自己的maven目錄));
invoker.execute(request);
如何安全的運行別人代碼呢?
- 利用java的沙箱機制
java沙箱介紹
java安全沙箱的前三類保證了jvm所運行程序的完整性,使得jvm不會因爲運行有漏洞或惡意的代碼而導致出現不可預期的狀態。而第四類沙箱模型是“類安全管理器及Java API”,它能保護jvm在運行有漏洞或惡意的代碼不會破壞外部資源。java通過稱爲安全管理器的一類API來保證這類安全性。
啓動沙箱的方法
- 在java進程啓動的時候,以命令的方式直接指定沙箱運行環境,具體的權限可以在安全策略文件中配置
- 用代碼的形式在程序中啓動
安全策略文件在 $JAVA_HOME/jre/lib/security/java.policy來指定訪問外部資源的權限,該策略文件也可以通過jvm參數-Djava.security.policy來指定。
Policy文件的主要格式如下:
keystore "some_keystore_url", "keystore_type";
grant [SignedBy "signer_names"] [, CodeBase "URL"] {
Permission permission_class_name ["target_name"] [,"action"] [,SignedBy"signer_names"];
… …
};
"keystore":一個keystore是一個私有密鑰(private keys)數據庫和相應的數字簽名
"grant":在Policy文件中的每一個grant記錄含有一個CodeSource(指定代碼)及其permission(許可)
// 啓用安全管理器
System.setSecurityManager(new SecurityManager());
問題
上面兩種辦法都有一個缺陷:一旦啓動之後就完全是沙箱了,但這並不符合我們的要求,沙箱只是運行用戶的那段代碼,之後還有很多代碼是和用戶代碼無關的,比如和主進程進行RPC通信,加載.class等操作。
解決問題
- 多進程(爲每個用戶起一個進程,並指定java程序運行的內存最大限制)
- 利用代理模式(System.setSecurityManager(自己實現一個SecurityManager);)
java -jar -Xms1024m -Xmx1024m XX.jar
Xms:表示jvm所需最小內存
Xmx:表示jvm所需最大內存
自己實現一個MySecurityManager繼承SecurityManager,裏面每次進行權限檢查的時候都會調用checkPermission方法,我們重寫這個方法,並在外面new一個SecurityManager,讓他在跑別人代碼的時候調用這個SecurityManager的checkPermission方法,跑我們的代碼的時候直接返回return;