我希望我的用户在用 Java 请求列表时能够编写自己的过滤器。
选项 1) 我正在考虑将 JavaScript 与 Rhino 结合使用。
我将用户的过滤器作为 JavaScript 字符串获取。然后在此脚本中调用 isAccepted(myItem)
。
根据回复我是否接受该元素。
选项 2) 我正在考虑 Groovy。
我的用户可以在文本字段中编写 Groovy 脚本。当我的用户使用此过滤器进行搜索时,Groovy 脚本会用 Java 进行编译(如果第一次调用)并调用 Java 方法 isAccepted()
根据回复我是否接受该元素。
我的应用程序很大程度上依赖于这种功能,并且它将在我的服务器上被集中调用。
所以我相信速度是关键。
选项 1 思考:
如果我错了,请纠正我,但我认为就我而言,Groovy 的主要优点是速度,但我的用户可以在我的服务器上编译和运行不需要的代码...(有任何解决方法吗?)
选项 2 思考:
我认为在大多数人看来 JavaScript 更像是一个玩具。即使这根本不是我的想法,但对于我的客户来说可能不会那么信任它。你也这么认为吗?
根据我在网上的阅读,我预计的另一个缺点是速度。
我的用户再次可以 access Java并在我的服务器上运行不需要的代码...(有解决方法吗?)
更多信息:
我正在 Google App Engine 上运行我的应用程序,作为我的应用程序的主要网络服务。
过滤器将通过调用应用 20 次。
过滤器(大多数时候)很简单。
有什么想法可以让这个过滤器对我的服务器安全吗?
还有其他方法可以使其发挥作用吗?
最佳答案
我的想法:
- 在编译脚本时,您必须使用自己的类加载器,以避免从脚本访问任何其他类。不确定这在 GAE 中是否可行。
- 您必须使用 Java 的 SecurityManager 功能来避免脚本能够访问文件系统、网络等。不确定这在 GAE 中是否可行。
仅看上面的两项,对我来说它看起来非常复杂和脆弱。如果您找不到现有的沙盒功能作为现有项目,则应该远离它。
设计一种允许您认为合法的表达式的领域特定语言要安全得多,并且查看上述项目,无论如何您都必须非常努力地思考您想要允许的内容。从那里到设计语言并不是一个很大的步骤。
小心不要用常规闭包(内部 DSL)来实现 DSL,因为那只是常规而且你也很容易被黑客攻击。您需要定义一种外部语言并解析它。我推荐解析器组合器 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())
}
关于java - 用户如何(安全地)用 Java 编写自己的过滤器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10162251/