0

我正在将 URL 与正则表达式进行匹配,测试它们是否反映了“关闭”命令。

这是一个执行关闭的 URL:

/exec?debug=true&command=shutdown&f=0

这是另一个执行关闭的合法但令人困惑的 URL:

/exec?commando=yes&zcommand=34&command=shutdown&p

现在,我必须确保只有一个command=...参数,它是command=shutdown。或者,我可以确保第一个 command=...参数是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(\&.*)*$

问题在于出现多个命令=...,其中第一个命令未关闭。

我尝试使用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 不需要登录。

但有时它会变得复杂。就像关闭命令不需要登录,而其他命令可以而且应该(奇怪的原因超出了我的问题范围)。

由于这是一个安全问题,我不能允许用户仅将 &command=shutdown 附加到 URL 并绕过过滤器。

所以我真的需要一个正则表达式,否则我需要重新定义配置规范。

4

8 回答 8

1

您需要分多个步骤执行此操作:

(1)找到匹配的^(?=\/exec\?).*?(?<=[?&])command=([^&]+)

(2)检查是否匹配shutdown

于 2012-10-05T16:27:12.157 回答
1

行。我感谢大家的精彩回答!我尝试了一些建议,与其他建议进行了斗争,总而言之,我不得不同意,即使存在正确的正则表达式,它看起来也很糟糕,不可维护,并且可以很好地作为一个令人讨厌的大学练习,但不是在真实的系统中配置。

我也意识到,由于这里涉及到一个过滤器,并且过滤器已经解析了它自己的 URI,所以将所有 URI 部分粘贴到一个字符串中并将其与正则表达式匹配是绝对荒谬的。我在想什么??

因此,我将重新设计过滤器及其配置。

非常感谢,人们!我很感激帮助:)

诺姆·罗特姆。

PS - 为什么我得到一个 userXXXX 昵称?很奇怪...

于 2012-10-06T23:24:58.360 回答
0

如果您可以接受第一场比赛,您可以使用'\\Wcommand=([^&]+)并获取第一组。

否则,您可以只调用Matcher.find两次来测试后续匹配,并最终使用第一个匹配,为什么要使用单个复杂的正则表达式来执行此操作?

于 2012-10-05T16:40:44.167 回答
0

如果这可以用一个正则表达式来完成,那很可能;它将非常复杂以至于无法阅读,因此无法维护,因为逻辑的意图将丢失。即使它被“记录在案”,对于只了解 Java 的人来说,它仍然不太明显。

解决这样的问题是对正则表达式的滥用,就像用锤子驱动螺丝一样,是在滥用锤子和螺丝。

更好的方法是使用URI对象解析整个事物、域和所有内容并提取查询参数,然后编写一个简单的循环遍历它们并根据您的业务逻辑决定什么是关闭,什么不是. 然后它将是简单的、自我记录的并且可能更有效(这不应该是一个问题)。

有些人在遇到问题时会想“我知道,我会使用正则表达式”。现在他们有两个问题。——杰米·扎温斯基

随心所欲地投反对票,但这个特定示例的最佳解决方案不是正则表达式;鉴于“澄清”更是如此。

尤其是在您必须与人共享代码的商业环境中,不仅现在与您合作,而且在未来与未知的人才库合作。“接受”的答案永远不应该通过公司代码审查。Zawinski 的引述正好适用于这种情况!

于 2012-10-05T16:46:31.230 回答
0

我不是 Java 编码员,但试试这个(在 Perl 中工作)>>

^(?=\/exec\?)(?:[^&]+(?<![?&]command)=[^&]+&)*(?<=[?&])command=shutdown(?:&|$)
于 2012-10-05T16:50:48.563 回答
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”限定符不是它们的目的,性能可能不是最好的。

于 2012-10-05T17:32:18.837 回答
0

这个经过测试(并经过充分评论)的正则表达式解决方案满足您的所有要求:

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 的更多信息,请参阅我的文章:正则表达式 URI 验证

于 2012-10-06T00:37:51.800 回答
-1

试试这个:

Pattern p = Pattern.compile(
    "^/exec\\?(?:(?:(?!\\1)command=shutdown()|(?!command=)\\w+(?:=[^&]+)?)(?:&|$))+$\\1");

或者更具可读性:

^/exec\?
(?:
  (?:
    (?!\1)command=shutdown()
    |
    (?!command=)\w+(?:=[^&]+)?
  )
  (?:&|$)
)+$
\1

正则表达式的主体是与关闭命令或名称为 not 的参数匹配的替代command。如果它确实与关闭命令匹配,则该分支中的空组“捕获”一个空字符串。它不需要消耗任何东西,因为我们只是将它用作一个复选框,以确认其中一个参数是关闭命令。

负前瞻 - (?!\1)- 阻止它匹配两个或多个关闭命令。我不知道这是否真的有必要,但这是一个很好的机会来演示(1)如何否定“反向断言”,以及(2)在某些情况下,反向引用可以出现在它所指的组之前(什么是称为前向参考)。

当整个 URL 被使用时,反向引用 ( \1) 就像一个零宽度断言。如果其中一个参数是command=shutdown,则反向引用将成功。否则即使它只是试图匹配一个空字符串也会失败,因为它引用的组没有参与匹配。

但我必须同意其他响应者的观点:当你的正则表达式变得如此复杂时,你应该认真考虑切换到不同的方法。


编辑: 它对我有用。这是演示

于 2012-10-05T20:47:03.643 回答