《Java中运行javascript代码(一)》介绍了如何在Java运行js代码,可是如果js代码是用户输入的,就存在可能输入错误或者恶意输入一些内容,比如无限循环、或者删除jvm所在机器上的文件。如何确保Java运行js时不会导致破坏性操作了。 我们可以使用sandbox,也就是delight-nashorn-sandbox。
注意事项:本博客所有代码是为了介绍相关内容而编写或者引用的,示例代码并非可直接用于生产的代码。仅供参考而已。
1,delight-nashorn-sandbox简介
官方介绍A secure sandbox for executing JavaScript in Java apps using the Nashorn engine.
delight-nashorn-sandbox是Java中使用Nashorn引擎执行JavaScript的安全沙箱。至于什么是沙箱请百度。
pom文件引入如下内容
<delight.nashorn.sandbox.version>0.1.14</delight.nashorn.sandbox.version>
...
<dependency>
<groupId>org.javadelight</groupId>
<artifactId>delight-nashorn-sandbox</artifactId>
<version>${delight.nashorn.sandbox.version}</version>
</dependency>
2 代码示例
可以对比着《Java中运行javascript代码(一)》的代码阅读。
package com.yq.js;
import com.alibaba.fastjson.JSONObject;
import delight.nashornsandbox.NashornSandbox;
import delight.nashornsandbox.NashornSandboxes;
import lombok.extern.slf4j.Slf4j;
import javax.script.Bindings;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.io.File;
import java.io.FileReader;
import java.io.Reader;
import java.io.StringWriter;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.util.concurrent.Executors;
/**
* Simple to Introduction
* className: JavaJSDemo
* https://github.com/javadelight/delight-nashorn-sandbox
* @author EricYang
* @version 2018/12/22 9:51
*/
@Slf4j
public class JavaJSSandBoxDemo {
private NashornSandbox sandbox = NashornSandboxes.create();
public static void main(String[] args) {
JavaJSSandBoxDemo demo = new JavaJSSandBoxDemo();
demo.bindingDemo();
demo.invokeFunctionDemo();
}
/*
* 初始化sandbox,限制它使用的资源
*/
JavaJSSandBoxDemo() {
sandbox.setMaxCPUTime(100);
//sandbox.setMaxMemory(50 * 1024);
sandbox.allowNoBraces(false);
sandbox.setMaxPreparedStatements(30); // because preparing scripts for execution is expensive
sandbox.setExecutor(Executors.newSingleThreadExecutor());
}
public void bindingDemo() {
try {
final Bindings bindings = sandbox.createBindings();
bindings.put("$ARG","hello world!");
Object result = sandbox.eval("$ARG", bindings);
log.info("result=" + result);
bindings.put("k",20);
result = sandbox.eval("k + 1", bindings);
log.info("result=" + result);
result = sandbox.eval("n = 1738");
log.info("result=" + result);
result = sandbox.get("n");
log.info("result=" + result);
Bindings scope = sandbox.createBindings();
scope.put("key", "西安");
result = sandbox.eval("key + '市'", scope);
log.info("result=" + result);
}
catch (ScriptException se) {
log.warn("binding demo exception.", se);
}
}
public boolean invokeFunctionDemo() {
log.info("--- invokeFunction ---" );
boolean result = true;
try {
final Bindings bindings = sandbox.createBindings();
bindings.put("msg","hello world!");
String str = "var user = {name:'张三',age:18,city:['陕西','台湾']};";
sandbox.eval(str, bindings);
log.info("Get msg={}", sandbox.eval("msg", bindings));
//获取变量
sandbox.eval("var sum = eval('1 + 2 + 3*4');");
//调用js的eval的方法完成运算
log.info("get sum={}", sandbox.get("sum"));
JSONObject msg = new JSONObject();
msg.put("temperature", 32);
msg.put("humidity", 20);
msg.put("voltage", 220);
msg.put("electricity", 13);
JSONObject metadata = new JSONObject();
metadata.put("deviceName", "空气质量检测器01");
metadata.put("contacts", "张三");
JSONObject msgType = new JSONObject();
msgType.put("type", "deviceTelemetryData1");
//定义函数
String func = "var result = true; \r\n" +
"if (msgType.type = 'deviceTelemetryData') { \r\n" +
" if (msg.temperature >0 && msg.temperature < 33) { \n result = true ;} \n" +
" else { \n result = false;} \n" +
"} else { \n result = false; \n" +
" var errorMsg = msgType.type + ' is not deviceTelemetryData'; \n" +
" print(msgType.type) \n } \n\n" +
"return result";
log.info("func = {}", func);
sandbox.eval("function filter(msg, metadata, msgType){ " + func + "}");
// 执行js函数
Object obj = sandbox.getSandboxedInvocable().invokeFunction("filter", msg, metadata, msgType);
//方法的名字,参数
log.info("function result={}", obj);
result = (Boolean)obj;
}
catch(Exception ex) {
log.warn("exception", ex);
result = false;
}
return result;
}
}
运行结果
14:30:56.405 [pool-1-thread-1] DEBUG delight.nashornsandbox.NashornSandbox - --- Running JS ---
14:30:56.409 [pool-1-thread-1] DEBUG delight.nashornsandbox.NashornSandbox - $ARG
14:30:56.409 [pool-1-thread-1] DEBUG delight.nashornsandbox.NashornSandbox - --- JS END ---
14:30:56.413 [main] INFO com.yq.js.JavaJSSandBoxDemo - result=hello world!
14:30:56.500 [pool-1-thread-1] DEBUG delight.nashornsandbox.NashornSandbox - --- Running JS ---
14:30:56.500 [pool-1-thread-1] DEBUG delight.nashornsandbox.NashornSandbox - k + 1
14:30:56.500 [pool-1-thread-1] DEBUG delight.nashornsandbox.NashornSandbox - --- JS END ---
14:30:56.503 [main] INFO com.yq.js.JavaJSSandBoxDemo - result=21.0
14:30:56.526 [pool-1-thread-1] DEBUG delight.nashornsandbox.NashornSandbox - --- Running JS ---
14:30:56.526 [pool-1-thread-1] DEBUG delight.nashornsandbox.NashornSandbox - n = 1738
14:30:56.526 [pool-1-thread-1] DEBUG delight.nashornsandbox.NashornSandbox - --- JS END ---
14:30:56.533 [main] INFO com.yq.js.JavaJSSandBoxDemo - result=1738
14:30:56.533 [main] INFO com.yq.js.JavaJSSandBoxDemo - result=1738
14:30:56.567 [pool-1-thread-1] DEBUG delight.nashornsandbox.NashornSandbox - --- Running JS ---
14:30:56.567 [pool-1-thread-1] DEBUG delight.nashornsandbox.NashornSandbox - key + '市'
14:30:56.567 [pool-1-thread-1] DEBUG delight.nashornsandbox.NashornSandbox - --- JS END ---
14:30:56.571 [main] INFO com.yq.js.JavaJSSandBoxDemo - result=西安市
14:30:56.571 [main] INFO com.yq.js.JavaJSSandBoxDemo - --- invokeFunction ---
14:30:56.683 [pool-1-thread-1] DEBUG delight.nashornsandbox.NashornSandbox - --- Running JS ---
14:30:56.683 [pool-1-thread-1] DEBUG delight.nashornsandbox.NashornSandbox - var user = {
name: '张三',
age: 18,
city: ['陕西', '台湾']
};
14:30:56.683 [pool-1-thread-1] DEBUG delight.nashornsandbox.NashornSandbox - --- JS END ---
14:30:56.694 [pool-1-thread-1] DEBUG delight.nashornsandbox.NashornSandbox - --- Running JS ---
14:30:56.697 [pool-1-thread-1] DEBUG delight.nashornsandbox.NashornSandbox - msg
14:30:56.697 [pool-1-thread-1] DEBUG delight.nashornsandbox.NashornSandbox - --- JS END ---
14:30:56.711 [main] INFO com.yq.js.JavaJSSandBoxDemo - Get msg=hello world!
14:30:56.721 [pool-1-thread-1] DEBUG delight.nashornsandbox.NashornSandbox - --- Running JS ---
14:30:56.721 [pool-1-thread-1] DEBUG delight.nashornsandbox.NashornSandbox - var sum = eval('1 + 2 + 3*4');
14:30:56.721 [pool-1-thread-1] DEBUG delight.nashornsandbox.NashornSandbox - --- JS END ---
14:30:56.730 [main] INFO com.yq.js.JavaJSSandBoxDemo - get sum=15
14:30:56.763 [main] INFO com.yq.js.JavaJSSandBoxDemo - func = var result = true;
if (msgType.type = 'deviceTelemetryData') {
if (msg.temperature >0 && msg.temperature < 33) {
result = true ;}
else {
result = false;}
} else {
result = false;
var errorMsg = msgType.type + ' is not deviceTelemetryData';
print(msgType.type)
}
return result
14:30:56.869 [pool-1-thread-1] DEBUG delight.nashornsandbox.NashornSandbox - --- Running JS ---
14:30:56.869 [pool-1-thread-1] DEBUG delight.nashornsandbox.NashornSandbox - var __it=Java.type('delight.nashornsandbox.internal.InterruptTest');var __if=function(){__it.test();};
function filter(msg, metadata, msgType) {__if();
var result = true;
if (msgType.type = 'deviceTelemetryData') {
if (msg.temperature > 0 && msg.temperature < 33) {
result = true;
} else {
result = false;
}
} else {
result = false;
var errorMsg = msgType.type + ' is not deviceTelemetryData';
print(msgType.type)
}
return result
}
14:30:56.869 [pool-1-thread-1] DEBUG delight.nashornsandbox.NashornSandbox - --- JS END ---
14:30:56.901 [main] INFO com.yq.js.JavaJSSandBoxDemo - function result=true
说明:如果我们在代码加上 sandbox.setMaxMemory(50*1024); 这一行限制运行js时使用的内存数量,就会报
16:59:11.083 [main] WARN com.yq.js.JavaJSSandBoxDemo - exception
javax.script.ScriptException: delight.nashornsandbox.exceptions.ScriptMemoryAbuseException: Script used more than the allowed [51200 B] of memory.
at delight.nashornsandbox.internal.NashornSandboxImpl$1.invokeFunction(NashornSandboxImpl.java:353)
at com.yq.js.JavaJSSandBoxDemo.invokeFunctionDemo(JavaJSSandBoxDemo.java:145)
at com.yq.js.JavaJSSandBoxDemo.main(JavaJSSandBoxDemo.java:39)
Caused by: delight.nashornsandbox.exceptions.ScriptMemoryAbuseException: Script used more than the allowed [51200 B] of memory.
at delight.nashornsandbox.internal.NashornSandboxImpl.executeSandboxedOperation(NashornSandboxImpl.java:169)
at delight.nashornsandbox.internal.NashornSandboxImpl.access$000(NashornSandboxImpl.java:36)
at delight.nashornsandbox.internal.NashornSandboxImpl$1.invokeFunction(NashornSandboxImpl.java:349)
... 2 common frames omitted