我正在将URL与正则表达式进行匹配,测试它们是否反映了“shutdown”命令。
这是执行关闭的URL:
/exec?debug=true&command=shutdown&f=0
这是执行关闭的另一个合法但令人困惑的URL:
/exec?commando=yes&zcommand=34&command=shutdown&p
现在,我必须确保只有一个 command = ... 参数,它是 command = shutdown 。或者,我可以确保第一个 命令= ... 参数 command = shutdown 。
这是我对所请求的正则表达式的测试:
/exec?version=0.4&command=shutdown&out=JSON&zcommand=1
应匹配
/exec?version=0.4&command=startup&out=JSON&zcommand=1&commando=shutdown
无法匹配
/exec?command=shutdown&out=JSON
应匹配
/exec?version=0.4&command=admin&out=JSON&zcommand=1&command=shutdown
无法匹配
这是我的基线 - 一个通过上述测试的正则表达式 - 除了最后一个以外的所有:
^/exec?(.*\&)*command=shutdown(\&.*)*$
问题是出现了多个command = ...,其中第一个没有关闭。
我尝试使用lookbehind:
^/exec?(.*\&)*(?<!(\&|\?)command=.*)command=shutdown(\&.*)*$
但我得到了:
Look-behind group does not have an obvious maximum length near index 31
我甚至尝试过原子分组。无济于事。我不能使下面的表达式不匹配:
/exec?version=0.4&command=admin&out=JSON&zcommand=1&command=shutdown
任何人都可以帮助处理通过所有测试的正则表达式吗?
我知道我欠你一些背景。
我的任务是配置一个过滤器来保护我们所有系统的servlet的入口,并验证是否有一个开放的HTTP会话(换句话说:已成功登录)。该过滤器还允许配置哪些URL不需要登录。
有些例外很简单:/ login不需要登录。对localhost的调用不需要登录。
但有时它会变得复杂。就像shutdown命令一样,不能要求登录,而其他命令可以而且应该(这个奇怪的原因超出了我的问题范围)。
由于这是一个安全问题,我不能允许用户只是将&amp; command = shutdown附加到URL并绕过过滤器。
所以我真的需要一个正则表达式,否则我需要重新定义配置规范。
答案 0 :(得分:1)
您需要在 多个 步骤中执行此操作:
(1) 查找^(?=\/exec\?).*?(?<=[?&])command=([^&]+)
的匹配
(2) 检查匹配是否为shutdown
答案 1 :(得分:1)
这个经过测试(并且完全注释)的正则表达式解决方案满足您的所有要求:
import java.util.regex.*;
public class TEST {
public static void main(String[] args) {
Pattern re = Pattern.compile(
" # Match URI having command=shutdown query variable value. \n" +
" ^ # Anchor to start of string. \n" +
" (?:[^:/?\\#\\s]+:)? # URI scheme (Optional). \n" +
" (?://[^/?\\#\\s]*)? # URI authority (Optional). \n" +
" [^?\\#\\s]* # URI path. \n" +
" \\? # Literal start of URI query. \n" +
" # Match var=value pairs preceding 'command=xxx'. \n" +
" (?: # Zero or more 'var=values' \n" +
" (?!command=) # only if not-'command=xxx'. \n" +
" [^&\\#\\s]* # Next var=value. \n" +
" & # var=value separator. \n" +
" )* # Zero or more 'var=values' \n" +
" command=shutdown # variable and value to match. \n" +
" # Match var=value pairs following 'command=shutdown'. \n" +
" (?: # Zero or more 'var=values' \n" +
" & # var=value separator. \n" +
" (?!command=) # only if not-'command=xxx'. \n" +
" [^&\\#\\s]* # Next var=value. \n" +
" )* # Zero or more 'var=values' \n" +
" (?:\\#\\S*)? # URI fragment (Optional). \n" +
" $ # Anchor to end of string.",
Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE | Pattern.COMMENTS);
String s = "/exec?version=0.4&command=shutdown&out=JSON&zcommand=1";
// Should match
// String s = "/exec?version=0.4&command=startup&out=JSON&zcommand=1&commando=shutdown";
// Should fail to match
// String s = "/exec?command=shutdown&out=JSON";
// Should match
// String s = "/exec?version=0.4&command=admin&out=JSON&zcommand=1&command=shutdown";
// Should fail to match";
Matcher m = re.matcher(s);
if (m.find()) {
// Successful match
System.out.print("Match found.\n");
} else {
// Match attempt failed
System.out.print("No match found.\n");
}
}
}
上述正则表达式匹配任何具有任何方案,权限,路径,查询或片段组件的RFC3986有效URI,但它必须具有一个(且仅一个)查询"command"
变量,其值必须完全相同,但不区分大小写:"shutdown"
。
精心制作的复杂正则表达式在使用适当的缩进和注释步骤(如上所示)编写时使用非常精细(并且可维护)。 (有关使用正则表达式验证URI的更多信息,请参阅我的文章:Regular Expression URI Validation)
答案 2 :(得分:1)
确定。我非常感谢你们的出色答案!我尝试了一些建议,与其他人斗争,总而言之,我必须同意,即使正确的正则表达式存在,它看起来很糟糕,不可维护,并且可以很好地作为一个讨厌的大学练习,但不是在一个真实的系统中配置。
我也意识到,由于此处涉及过滤器,并且过滤器已经解析了自己的URI,因此将所有URI部分粘合到字符串中并将其与正则表达式进行匹配绝对是荒谬的。我在想什么?
因此我将重新设计过滤器及其配置。
非常感谢,人们!我很感激帮助:)
Noam Rotem。
P.S。 - 为什么我会收到userXXXX缺口?很奇怪......
答案 3 :(得分:0)
如果你可以接受第一场比赛,你可以使用'\\Wcommand=([^&]+)
并获取第一组。
否则,您可以只调用Matcher.find
两次以测试后续匹配,并最终使用第一个匹配,为什么要使用单个复杂正则表达式执行此操作?
答案 4 :(得分:0)
如果可以使用单个正则表达式完成此操作,那么很可能;由于逻辑的意图将会丢失,因此它将变得如此复杂,以至于无法读取,因而无法维护。即使它是“记录”的,对于刚认识Java的人来说,它仍然不那么明显。
解决这样的问题就是滥用正则表达式 因为用锤子驱动螺钉会滥用锤子和螺钉 两者。
更好的方法是使用URI
对象解析整个事物,域和所有内容并拉出查询参数,然后编写一个简单的循环,遍历它们并根据您的业务逻辑决定什么是关闭,什么不是。然后它将是简单的,自我记录的,可能更有效(不应该是一个问题)。
有些人在遇到问题时会想“我知道,我会用 正则表达式。“现在他们有两个问题。 - Jamie Zawinski
向下投票你想要的所有,但这个特定例子的最佳解决方案不是正则表达式;鉴于“澄清”更是如此。
特别是在您必须与人共享代码的商业环境中,不仅要与您合作,还要在未来与未知的人才库合作。 “接受”的答案绝不应该通过公司代码审查。 Zawinski的报价恰恰适用于这种情况!
答案 5 :(得分:0)
我不是Java编码器,但尝试这个(在Perl中工作)&gt;&gt;
^(?=\/exec\?)(?:[^&]+(?<![?&]command)=[^&]+&)*(?<=[?&])command=shutdown(?:&|$)
答案 6 :(得分:0)
要匹配第一次出现的command = shutdown,请使用:
Pattern.compile("^((?!command=).)+command=shutdown.*$");
结果如下:
"/exec?version=0.4&command=shutdown&out=JSON&zcommand=1" => false
"/exec?command=shutdown&out=JSON" => true
"/exec?version=0.4&command=startup&out=JSON&zcommand=1&commando=shutdown" => false
"/exec?commando=yes&zcommand=34&command=shutdown&p" => false
如果要匹配仅包含一个'command ='的字符串,请使用:
Pattern.compile("^((?!command=).)+command=shutdown((?!command=).)+$");
请注意,在正则表达式中使用“not”限定符不是它们的目的,性能可能不是最好的。
答案 7 :(得分:-1)
试试这个:
Pattern p = Pattern.compile(
"^/exec\\?(?:(?:(?!\\1)command=shutdown()|(?!command=)\\w+(?:=[^&]+)?)(?:&|$))+$\\1");
或者更可读:
^/exec\?
(?:
(?:
(?!\1)command=shutdown()
|
(?!command=)\w+(?:=[^&]+)?
)
(?:&|$)
)+$
\1
正则表达式的主体是一个交替,它匹配关闭命令或名称不是command
的参数。如果它与shutdown命令匹配,则该分支中的空组“捕获”空字符串。它不需要消耗任何东西,因为我们只将它用作复选框,确认 en passant 其中一个参数是关闭命令。
否定前瞻 - (?!\1)
- 阻止它匹配两个或多个关闭命令。我不知道这是否真的有必要,但这是一个很好的机会来证明(1)如何否定“反向断言”,以及(2)反向引用可以出现在它在某些情况下引用的组之前(什么是称为前向参考)。
当使用整个URL时,反向引用(\1
)的作用类似于零宽度断言。如果其中一个参数为command=shutdown
,则反向引用将成功。否则它会失败,即使它只是尝试匹配一个空字符串,因为它引用的组没有参与匹配。
但我必须同意其他响应者:当你的正则表达式变得复杂时,你应该认真考虑改用不同的方法。
编辑:它对我有用。这是 demo 。