0


Groovy安全高效的执行(死循环,休眠,危险方法)

背景

在很多场景下有需要执行异步任务,或者执行用户的自定义任务时,通常我们会使用Groovy脚本能力来完成任务。通过groovy动态脚本能力,在业务执行过程中动态执行不同业务线或者用户的脚本,来满足不同需求。
这样可以非常方便的进行业务拓展,但是也会带来一系列安全问题,
1 比如在脚本中调用了系统危险的方法,如System.exit 会导致整个服务停止
2 触发了死循环等场景,会导致任务卡死,使用多线程的话线程也很块就被占完。
3 使用Thread.sleep 将线程进行休眠

解决方案

关于以上三类问题,这里也进行了归纳总结,给出对应的方案

死循环执行

1 先定义一个死循环执行脚本,功能就是一直打印就可以了

privatestaticString script ="import groovy.transform.TimedInterrupt\n"+"\n"+"import java.util.concurrent.TimeUnit\n"+"\n"+"class GroovyScriptTest {\n"+"    public String execute(String key) {\n"+"        while (true) {\n"+"            print(11);\n"+"        }\n"+"        return key + \":updated\";\n"+"    }\n"+"}\n";

为了方便查看生成后的源码,这里将生成的目录设置为target目录下

publicclassGroovyClassLoaderTest2{privatestaticString script ="import groovy.transform.TimedInterrupt\n"+"\n"+"import java.util.concurrent.TimeUnit\n"+"\n"+"class GroovyScriptTest {\n"+"\n"+"    public String execute(String key) {\n"+"        while (true) {\n"+"            print(11);\n"+"        }\n"+"        return key + \":updated\";\n"+"    }\n"+"}\n";publicstaticvoidmain(String[] args)throwsInstantiationException,IllegalAccessException{CompilerConfiguration config =newCompilerConfiguration();
        config.setTargetDirectory(GroovyClassLoaderTest2.class.getClassLoader().getResource("./").getPath());// 重置调用时间GroovyClassLoader groovyClassLoader =newGroovyClassLoader(GroovyClassLoaderTest2.class.getClassLoader(), config);Class aClass = groovyClassLoader.parseClass(script);GroovyObject groovyObject =(GroovyObject) aClass.newInstance();Object o = groovyObject.invokeMethod("execute","key");System.out.println("groovy执行结果:"+ o);}}

可以看到 运行起来后控制台一直在输出1,在实际业务场景中,我们需要对这种行为进行管控,可以使用Groovy自带的注解

@TimedInterrupt(unit =TimeUnit.MILLISECONDS, value =1000L)

这里可以对方法进行执行时间设置,可以指定执行时间单位和时间,比如这里设置执行为1000ms,到时间后任务将自动结束
在这里插入图片描述
这样就达到了我们需要的效果。

Thread.sleep问题

在脚本中使用了指定睡眠时间的场景,如适用Thread.sleep(100000)会严重的拖慢了整体执行效率,此时可以通过Groovy自带的机制

@groovy.transform.ThreadInterrupt

添加此注解后,我们可以主动设置线程为已中断的,如果使用线程池的话可以使用futuretask的cancel(true)方式超时中断线程

危险方法调用

这里有两种方式,一种是通过执行过程中的拦截器进行处理,可以查看GroovyInterceptor,还有就是在编译期间就识别出来危险方法,在前置阶段进行拦截(SecureASTCustomizer.ExpressionChecker),核心代码如下:

publicstaticclassNoSupportClassTestimplementsSecureASTCustomizer.ExpressionChecker{@OverridepublicbooleanisAuthorized(Expression expression){if(expression instanceofMethodCallExpression){MethodCallExpression mc =(MethodCallExpression) expression;String className = mc.getReceiver().getText();String method = mc.getMethodAsString();System.out.println("=====>"+className +"."+ method);}returntrue;}}

只要识别到需要拦截的方法,这里返回false就可以进行前置拦截。

原理分析

通过以上两种方式,可以对循环(for,while)和线程睡眠的方式进行拦截处理,到target目录下查看生成的class文件,在方法执行的时候会先判断线程是否已经被中断了,在每个循环执行的时候会判断下执行时间,这样组合起来就可以非常好的达到了我们需要的业务效果。在这里插入图片描述

完整代码

publicclassGroovyClassLoaderTest3{privatestaticString script ="import groovy.transform.TimedInterrupt\n"+"\n"+"import java.util.concurrent.TimeUnit\n"+"\n"+"class GroovyScriptTest {\n"+"\n"+"    @TimedInterrupt(unit = TimeUnit.MILLISECONDS, value = 1000L)\n"+"    @groovy.transform.ThreadInterrupt\n"+"    public String execute(String key) {\n"+"        while (true) {\n"+"            print(11);\n"+"        }\n"+"        return key + \":updated\";\n"+"    }\n"+"}\n";publicstaticvoidmain(String[] args)throwsInstantiationException,IllegalAccessException{CompilerConfiguration config =newCompilerConfiguration();
        config.setTargetDirectory(GroovyClassLoaderTest3.class.getClassLoader().getResource("./").getPath());SecureASTCustomizer secure =newSecureASTCustomizer();
        secure.addExpressionCheckers(newNoSupportClassTest());
        config.addCompilationCustomizers(secure);// 重置调用时间GroovyClassLoader groovyClassLoader =newGroovyClassLoader(GroovyClassLoaderTest3.class.getClassLoader(), config);Class aClass = groovyClassLoader.parseClass(script);GroovyObject groovyObject =(GroovyObject) aClass.newInstance();Object o = groovyObject.invokeMethod("execute","key");System.out.println("groovy执行结果:"+ o);}publicstaticclassNoSupportClassTestimplementsSecureASTCustomizer.ExpressionChecker{@OverridepublicbooleanisAuthorized(Expression expression){System.out.println(expression);if(expression instanceofMethodCallExpression){MethodCallExpression mc =(MethodCallExpression) expression;String className = mc.getReceiver().getText();String method = mc.getMethodAsString();System.out.println("=====>"+className +"."+ method);}returntrue;}}}
标签: java jvm

本文转载自: https://blog.csdn.net/believer123/article/details/128445870
版权归原作者 cmlbeliever 所有, 如有侵权,请联系我们删除。

“Groovy安全高效的执行(死循环,休眠,危险方法)”的评论:

还没有评论