Java中运行javascript代码(二) 更安全运行js代码

《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
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值