我希望我的用户在Java中请求List时能够编写自己的过滤器。
选项1)我正在考虑使用Rhino的JavaScript
我将用户的过滤器作为javascript字符串。然后在此脚本中调用isAccepted(myItem)
根据回复我是否接受该元素。
选项2)我正在考虑Groovy
我的用户可以在文本字段中编写Groovy脚本。当我的用户使用此过滤器搜索时,Groovy脚本是用Java编译的(如果是第一次调用)并调用Java方法isAccepted()
根据回复我是否接受该元素。
我的应用程序非常依赖于这个功能,它会在我的服务器上集中调用
因此,我认为速度是关键。
选项1思考:
如果我错了,请纠正我,但我认为在我的情况下,Groovy的主要优点是速度,但我的用户可以在我的服务器上编译和运行不需要的代码...(任何解决方法?)
选项2思考: 我认为在大多数人看来,JavaScript更像是玩具。即使不是我的想法根本,也许对我的客户来说,他们不会那么信任它。你这么认为吗? 我期待的另一个坏点是速度,来自我在网上的阅读 我的用户可以access Java再次在我的服务器上运行不需要的代码......(任何解决方法?)
更多信息:
我在Google App Engine上运行我的应用程序,用于我的应用程序的主要Web服务
过滤器将通过呼叫应用20次
过滤器(大部分时间)都很简单
是否有任何想法让我的服务器安全过滤? 还有其他任何方法可以让它起作用吗?
答案 0 :(得分:1)
一些想法:
无论您使用的是JavaScript还是Groovy,它都将在您提供给脚本的上下文中运行,因此脚本不应该能够访问您不想要它的任何内容(当然,你应该进行广泛的测试,以确定是否采用这条路线。)
如果可能的话,将过滤器表达式指定为数据而不是可执行代码,可能会更安全。当然,这取决于过滤器表达式的复杂程度。也许您可以将表示分解为字段,比较器和值等类似的东西,可以作为数据处理并以常规方式进行评估?
如果您担心用户可以通过脚本语言注入什么,那么使用JavaScript可能会更安全。我不认为性能应该是一个问题,但我再次建议进行广泛的测试。
答案 1 :(得分:1)
我的想法:
只关注上面的两个项目,它看起来非常复杂和脆弱。如果您无法将现有沙盒功能作为现有项目找到,则应远离它。
设计一种特定于领域的语言,允许您认为合法的表达方式更加安全,并且在查看上述内容时,您必须非常努力地思考您想要允许的内容。从那里到设计语言不是一大步。
小心不要使用groovy闭包(内部DSL)来实现DSL,因为这只是时髦而你也是可以破解的。您需要定义一个extrnal语言并解析它。我建议使用解析器组合器jparsec来定义语法。在这种情况下,不需要编译器编译器。
仅供参考,这是我用jparsec(groovy代码)编写的一个小解析器:
//import some static methods, this will allow more concise code
import static org.codehaus.jparsec.Parsers.*
import static org.codehaus.jparsec.Terminals.*
import static org.codehaus.jparsec.Scanners.*
import org.codehaus.jparsec.functors.Map as FMap
import org.codehaus.jparsec.functors.Map4 as FMap4
import org.codehaus.jparsec.functors.Map3 as FMap3
import org.codehaus.jparsec.functors.Map2 as FMap2
/**
* Uses jparsec combinator parser library to construct an external DSL parser for the following grammar:
* <pre>
* pipeline := routingStep*
* routingStep := IDENTIFIER '(' parameters? ')'
* parameters := parameter (',' parameter)*
* parameter := (IDENTIFIER | QUOTED_STRING) ':' QUOTED_STRING
* </pre>
*/
class PipelineParser {
//=======================================================
//Pass 1: Define which terminals are part of the grammar
//=======================================================
//operators
private static def OPERATORS = operators(',', '(', ')', ':')
private static def LPAREN = OPERATORS.token('(')
private static def RPAREN = OPERATORS.token(')')
private static def COLON = OPERATORS.token(':')
private static def COMMA = OPERATORS.token(',')
//identifiers tokenizer
private static def IDENTIFIER = Identifier.TOKENIZER
//single quoted strings tokenizer
private static def SINGLE_QUOTED_STRING = StringLiteral.SINGLE_QUOTE_TOKENIZER
//=======================================================
//Pass 2: Define the syntax of the grammar
//=======================================================
//PRODUCTION RULE: parameter := (IDENTIFIER | QUOTED_STRING) ':' QUOTED_STRING
@SuppressWarnings("GroovyAssignabilityCheck")
private static def parameter = sequence(or(Identifier.PARSER,StringLiteral.PARSER), COLON, StringLiteral.PARSER, new FMap3() {
def map(paramName, colon, paramValue) {
new Parameter(name: paramName, value: paramValue)
}
})
//PRODUCTION RULE: parameters := parameter (',' parameter)*
@SuppressWarnings("GroovyAssignabilityCheck")
private static def parameters = sequence(parameter, sequence(COMMA, parameter).many(), new FMap2() {
def map(parameter1, otherParameters) {
if (otherParameters != null) {
[parameter1, otherParameters].flatten()
} else {
[parameter1]
}
}
})
//PRODUCTION RULE: routingStep := IDENTIFIER '(' parameters? ')'
@SuppressWarnings("GroovyAssignabilityCheck")
private static def routingStep = sequence(Identifier.PARSER, LPAREN, parameters.optional(), RPAREN, new FMap4() {
def map(routingStepName, lParen, parameters, rParen) {
new RoutingStep(
name: routingStepName,
parameters: parameters ?: []
)
}
})
//PRODUCTION RULE: pipeline := routingStep*
@SuppressWarnings("GroovyAssignabilityCheck")
private static def pipeline = routingStep.many().map(new FMap() {
def map(from) {
new Pipeline(
routingSteps: from
)
}
})
//Combine the above tokenizers to create the tokenizer that will parse the stream and spit out the tokens of the grammar
private static def tokenizer = or(OPERATORS.tokenizer(), SINGLE_QUOTED_STRING, IDENTIFIER)
//This parser will be used to define which input sequences need to be ignored
private static def ignored = or(JAVA_LINE_COMMENT, JAVA_BLOCK_COMMENT, WHITESPACES)
/**
* Parser that is used to parse extender pipelines.
* <pre>
* def parser=PipelineParser.parser
* Pipeline pipeline=parser.parse(pipelineStr)
* </pre>
* Returns an instance of {@link Pipeline} containing the AST representation of the parsed string.
*/
//Create a syntactic pipeline parser that will use the given tokenizer to parse the input into tokens, and will ignore sequences that are matched by the given parser.
static def parser = pipeline.from(tokenizer, ignored.skipMany())
}
答案 2 :(得分:0)
我永远不会让用户输入任意代码。这是脆弱,不安全和糟糕的用户体验。我不知道你的用户是什么,我猜你会花很多时间来回答问题。如果你的大部分过滤器很简单,为什么不为它们创建一个小过滤器构建器呢?
至于groovy与JavaScript我认为groovy更容易理解,更适合脚本,但这只是我的意见。