重写阀

简介

重写阀以与 Apache HTTP Server 中的 mod_rewrite 非常相似的方式实现 URL 重写功能。

配置

重写阀使用 org.apache.catalina.valves.rewrite.RewriteValve 类名作为阀进行配置。

重写阀可以配置为在主机中添加的阀。有关如何配置它的信息,请参见 virtual-server 文档。它将使用包含重写指令的 rewrite.config 文件,该文件必须放置在主机配置文件夹中。

它也可以位于 Web 应用程序的 context.xml 中。然后,该阀将使用包含重写指令的 rewrite.config 文件,该文件必须放置在 Web 应用程序的 WEB-INF 文件夹中

指令

rewrite.config 文件包含一系列指令,这些指令与 mod_rewrite 使用的指令非常相似,特别是中心 RewriteRule 和 RewriteCond 指令。以 # 字符开头的行将被视为注释并被忽略。

注意:本节是 mod_rewrite 文档的修改版本,该文档的版权为 1995-2006 Apache Software Foundation 所有,并根据 Apache 许可证版本 2.0 获得许可。

RewriteCond

语法:RewriteCond TestString CondPattern

RewriteCond 指令定义规则条件。一个或多个 RewriteCond 可以出现在 RewriteRule 指令之前。只有当 URI 的当前状态与其模式匹配,并且满足这些条件时,才会使用以下规则。

TestString 是一个字符串,除了纯文本之外,还可以包含以下扩展构造

  • RewriteRule 反向引用:这些反向引用采用 $N(0 <= N <= 9)的形式,可访问模式的分组部分(括号中),从受当前 RewriteCond 条件集约束的 RewriteRule
  • RewriteCond 反向引用:这些反向引用采用 %N(1 <= N <= 9)的形式,可访问模式的分组部分(同样在括号中),从当前条件集中匹配的最后一个 RewriteCond
  • RewriteMap 扩展:这些扩展采用 ${mapname:key|default} 的形式。有关更多详细信息,请参见 RewriteMap 的文档
  • 服务器变量:这些变量采用 %{ NAME_OF_VARIABLE } 的形式,其中 NAME_OF_VARIABLE 可以是从以下列表中获取的字符串
    • HTTP 标头

      HTTP_USER_AGENT
      HTTP_REFERER
      HTTP_COOKIE
      HTTP_FORWARDED
      HTTP_HOST
      HTTP_PROXY_CONNECTION
      HTTP_ACCEPT

    • 连接和请求

      REMOTE_ADDR
      REMOTE_HOST
      REMOTE_PORT
      REMOTE_USER
      REMOTE_IDENT
      REQUEST_METHOD
      SCRIPT_FILENAME
      REQUEST_PATH
      CONTEXT_PATH
      SERVLET_PATH
      PATH_INFO
      QUERY_STRING
      AUTH_TYPE

    • 服务器内部

      DOCUMENT_ROOT
      SERVER_NAME
      SERVER_ADDR
      SERVER_PORT
      SERVER_PROTOCOL
      SERVER_SOFTWARE

    • 日期和时间

      TIME_YEAR
      TIME_MON
      TIME_DAY
      TIME_HOUR
      TIME_MIN
      TIME_SEC
      TIME_WDAY
      TIME

    • 特殊项

      THE_REQUEST
      REQUEST_URI
      REQUEST_FILENAME
      HTTPS

    这些变量都对应于同名的 HTTP MIME 标头和 Servlet API 方法。大多数在手册或 CGI 规范中的其他地方有记录。那些对重写阀门来说很特殊的内容包括以下内容。

    REQUEST_PATH
    对应于用于映射的完整路径。
    CONTEXT_PATH
    对应于映射上下文的路径。
    SERVLET_PATH
    对应于 servlet 路径。
    THE_REQUEST
    浏览器发送到服务器的完整 HTTP 请求行(例如,“GET /index.html HTTP/1.1”)。这不包括浏览器发送的任何其他标头。
    REQUEST_URI
    HTTP 请求行中请求的资源。(在上面的示例中,这将是“/index.html”。)
    REQUEST_FILENAME
    与请求匹配的文件或脚本的完整本地文件系统路径。
    HTTPS
    如果连接使用 SSL/TLS,则包含文本“on”,否则包含“off”。

您应该注意的其他事项

  1. 变量 SCRIPT_FILENAME 和 REQUEST_FILENAME 包含相同的值 - Apache 服务器的内部 request_rec 结构的 filename 字段的值。第一个名称是众所周知的 CGI 变量名称,而第二个名称是 REQUEST_URI(包含 request_rec 的 uri 字段的值)的适当对应项。
  2. %{ENV:variable},其中 variable 可以是任何 Java 系统属性,也可用。
  3. %{SSL:variable},其中 variable 是 SSL 环境变量的名称,已实现,但 SSL_SESSION_RESUMEDSSL_SECURE_RENEGSSL_COMPRESS_METHODSSL_TLS_SNISSL_SRP_USERSSL_SRP_USERINFOSSL_CLIENT_VERIFYSSL_CLIENT_SAN_OTHER_msUPN_nSSL_CLIENT_CERT_RFC4523_CEASSL_SERVER_SAN_OTHER_dnsSRV_n 除外。当使用 OpenSSL 时,与服务器证书相关的变量(以 SSL_SERVER_ 为前缀)不可用。示例:%{SSL:SSL_CIPHER_USEKEYSIZE} 可能扩展为 128
  4. %{HTTP:header},其中 header 可以是任何 HTTP MIME 标头名称,始终可用于获取 HTTP 请求中发送的标头值。示例:%{HTTP:Proxy-Connection} 是 HTTP 标头 'Proxy-Connection:' 的值。

CondPattern 是条件模式,一个应用于 TestString 当前实例的正则表达式。在与 CondPattern 匹配之前,首先会计算 TestString

记住: CondPattern 是一个 与 perl 兼容的正则表达式,并增加了一些内容

  1. 您可以在模式字符串前加上 '!' 字符(感叹号)以指定匹配的模式。
  2. CondPatterns 有些特殊变体。除了真正的正则表达式字符串,您还可以使用以下内容之一
    • '<CondPattern'(按词典顺序排列)
      CondPattern 视为普通字符串,并按词典顺序将其与 TestString 进行比较。如果 TestString 按词典顺序在 CondPattern 之前,则为真。
    • '>CondPattern'(按词典顺序排列)
      CondPattern 视为普通字符串,并按词典顺序将其与 TestString 进行比较。如果 TestString 按词典顺序在 CondPattern 之后,则为真。
    • '=CondPattern'(按词典顺序相等)
      CondPattern 视为普通字符串,并按词典顺序将其与 TestString 进行比较。如果 TestString 按词典顺序等于 CondPattern(两个字符串完全相等,逐个字符相等),则为真。如果 CondPattern""(两个引号),则将 TestString 与空字符串进行比较。
    • '-d'(是directory)
      TestString 视为路径名,并测试它是否存在且是目录。
    • '-f'(是常规file)
      TestString 视为路径名,并测试它是否存在且是常规文件。
    • '-s'(是常规文件,有size)
      TestString 视为路径名,并测试它是否存在且是常规文件,且大小大于零。
    注意:所有这些测试也可以加上感叹号 ('!') 来否定其含义。
  3. 你还可以通过将 [flags] 附加为 RewriteCond 指令的第三个参数,为 CondPattern 设置特殊标志,其中 flags 是以下任何标志的逗号分隔列表
    • 'nocase|NC'(no case)
      这使得测试不区分大小写 - 忽略 TestStringCondPattern 中 'A-Z' 和 'a-z' 之间的差异。此标志仅对 TestStringCondPattern 之间的比较有效。它对文件系统和子请求检查没有影响。
    • 'ornext|OR'(or 下一个条件)
      使用此功能将规则条件与本地 OR 而不是隐式 AND 结合使用。典型示例
      RewriteCond %{REMOTE_HOST}  ^host1.*  [OR]
      RewriteCond %{REMOTE_HOST}  ^host2.*  [OR]
      RewriteCond %{REMOTE_HOST}  ^host3.*
      RewriteRule ...some special stuff for any of these hosts...
      如果没有此标志,你将不得不三次编写条件/规则对。

示例

要根据请求的 'User-Agent:' 标头重写网站的主页,可以使用以下内容

RewriteCond  %{HTTP_USER_AGENT}  ^Mozilla.*
RewriteRule  ^/$                 /homepage.max.html  [L]

RewriteCond  %{HTTP_USER_AGENT}  ^Lynx.*
RewriteRule  ^/$                 /homepage.min.html  [L]

RewriteRule  ^/$                 /homepage.std.html  [L]

解释:如果你使用自称为 'Mozilla' 的浏览器(包括 Netscape Navigator、Mozilla 等),那么你将获得最大主页(可能包括框架或其他特殊功能)。如果你使用 Lynx 浏览器(基于终端),那么你将获得最小主页(可能是一个易于浏览的仅文本版本)。如果这两个条件都不适用(你使用任何其他浏览器,或者你的浏览器将自己标识为非标准),你将获得 std(标准)主页。

RewriteMap

语法:RewriteMap name rewriteMapClassName optionalParameters

rewriteMapClassName 值还允许特殊值

  • int:toupper:将传递的值转换为大写的特殊映射
  • int:tolower:将传递的值转换为小写的特殊映射
  • int:escape:URL 转义传递的值
  • int:unescape:URL 取消转义传递的值

这些映射是使用用户必须实现的接口实现的。它的类名为 org.apache.catalina.valves.rewrite.RewriteMap,其代码为

package org.apache.catalina.valves.rewrite;

public interface RewriteMap {
    default String setParameters(String params...); // calls setParameters(String) with the first parameter if there is only one
    public String setParameters(String params);
    public String lookup(String key);
}

此类的一个引用实现 – 在我们的示例中为 rewriteMapClassName – 将通过调用 setParameters(String) 实例化并使用可选参数(即上述 optionalParameters,注意空格)初始化。然后,该实例将以 RewriteMap 规则的第一个参数作为名称进行注册。

注意:您可以使用多个参数。这些参数必须用空格分隔。参数可以使用 " 引号引起来。这可以在参数中启用空格字符。

通过调用 lookup(String),将向该映射实例提供在相应的 RewriteRule 中配置的查找值。您的实现可以自由地返回 null 以指示应使用给定的默认值,或返回替换值。

假设您想实现一个重写映射函数,该函数将所有查找键转换为大写。您将首先实现一个实现 RewriteMap 接口的类。

package example.maps;

import org.apache.catalina.valves.rewrite.RewriteMap;

public class UpperCaseMap implements RewriteMap {

  @Override
  public String setParameters(String params) {
    // nothing to be done here
    return null;
  }

  @Override
  public String lookup(String key) {
    if (key == null) {
      return null;
    }
    return key.toUpperCase();
  }

}

编译此类,将其放入 jar 中,并将该 jar 放置在 ${CATALINA_BASE}/lib 中。

完成上述操作后,您现在可以使用 RewriteMap 指令定义一个映射,并在 RewriteRule 中进一步使用该映射。

RewriteMap uc example.maps.UpperCaseMap

RewriteRule ^/(.*)$ ${uc:$1}

使用此设置,对 URL 路径 /index.html 的请求将被路由到 /INDEX.HTML

RewriteRule

语法:RewriteRule Pattern Substitution

RewriteRule 指令是真正的重写主力。该指令可以多次出现,每个实例定义一个重写规则。定义这些规则的顺序很重要 - 这是它们在运行时应用的顺序。

Pattern 是一个与 perl 兼容的正则表达式,应用于当前 URL。“当前”表示应用此规则时的 URL 值。这可能不是最初请求的 URL,该 URL 可能已匹配先前的规则并已被更改。

安全警告:由于 Java 的正则表达式匹配方式,格式错误的正则表达式模式容易受到“灾难性回溯”的攻击,也称为“正则表达式拒绝服务”或 ReDoS。因此,应格外小心使用 RewriteRule 模式。一般来说,很难自动检测到此类容易受到攻击的正则表达式,因此一个好的防御措施是对灾难性回溯的主题进行一些了解。一个很好的参考是 OWASP ReDoS 指南

有关正则表达式语法的提示

Text:
  .           Any single character
  [chars]     Character class: Any character of the class 'chars'
  [^chars]    Character class: Not a character of the class 'chars'
  text1|text2 Alternative: text1 or text2

Quantifiers:
  ?           0 or 1 occurrences of the preceding text
  *           0 or N occurrences of the preceding text (N > 0)
  +           1 or N occurrences of the preceding text (N > 1)

Grouping:
  (text)      Grouping of text
              (used either to set the borders of an alternative as above, or
              to make backreferences, where the Nth group can
              be referred to on the RHS of a RewriteRule as $N)

Anchors:
  ^           Start-of-line anchor
  $           End-of-line anchor

Escaping:
  \char       escape the given char
              (for instance, to specify the chars ".[]()" etc.)

有关正则表达式的更多信息,请参阅 perl 正则表达式手册页(“perldoc perlre”)。如果您有兴趣了解有关正则表达式及其变体(POSIX 正则表达式等)的更详细信息,则以下书籍专门针对此主题

Mastering Regular Expressions, 2nd Edition
Jeffrey E.F. Friedl
O'Reilly & Associates, Inc. 2002
ISBN 978-0-596-00289-3

在规则中,NOT 字符(“!”)也可作为可能的模式前缀。这使您能够否定一个模式;例如,说:“如果当前 URL 匹配此模式”。这可用于特殊情况,在这些情况下,匹配否定模式更容易,或作为最后一个默认规则。

注意:当使用 NOT 字符否定模式时,您不能在该模式中包含分组通配符部分。这是因为,当模式不匹配(即否定匹配)时,组中没有内容。因此,如果使用了否定模式,您不能在替换字符串中使用 $N

重写规则的替换是替换(或取代)与模式匹配的原始 URL 的字符串。除了纯文本外,它还可以包括

  1. 反向引用($N)到 RewriteRule 模式
  2. 反向引用(%N)到上次匹配的 RewriteCond 模式
  3. 服务器变量,如规则条件测试字符串(%{VARNAME}
  4. 映射函数调用(${mapname:key|default}

反向引用是形式为 $NN=0..9)的标识符,它将被匹配的模式的第 N 组的内容替换。服务器变量与 RewriteCond 指令的 TestString 相同。映射函数来自 RewriteMap 指令,并在其中进行了解释。这三种类型的变量按上述顺序展开。

如前所述,所有重写规则都应用于替换(按它们在配置文件中定义的顺序)。URL 被替换完全取代,并且重写过程将继续,直到应用所有规则,或通过 L 标志明确终止它。

特殊字符 $% 可以通过在它们前面加上反斜杠字符 \ 来引用。

有一个名为“-”的特殊替换字符串,它表示:不替换!这在提供仅匹配 URL 但不替换任何内容的重写规则时非常有用。它通常与 C(链)标志结合使用,以便在进行替换之前应用多个模式。

与较新的 mod_rewrite 版本不同,Tomcat 重写阀门不支持绝对 URL(必须使用特定的重定向标志才能指定绝对 URL,请参见下文)或直接文件服务。

此外,您可以通过将 [标志] 作为 RewriteRule 指令的第三个参数附加来为替换设置特殊 标志标志是以逗号分隔的以下任何标志的列表

  • chain|C”(与下一条规则chain)
    此标志将当前规则与下一条规则链接(下一条规则本身可以与下一条规则链接,依此类推)。这具有以下效果:如果一条规则匹配,则处理将照常进行 - 该标志不起作用。如果规则匹配,则将跳过所有后续链接规则。例如,当您让外部重定向发生(其中不应出现“.www”部分!)时,可以在每个目录规则集中使用它来删除“.www”部分。
  • cookie|CO=NAME:VAL:domain[:lifetime[:path]]”(设置cookie)
    这会在客户端浏览器中设置一个 cookie。cookie 的名称由 NAME 指定,值是 VALdomain 字段是 cookie 的域,例如“。apache.org”,可选的 lifetime 是 cookie 的生命周期(以分钟为单位),可选的 path 是 cookie 的路径
  • env|E=VAR:VAL”(设置environment 变量)
    这强制将名为 VAR 的请求属性设置为值 VAL,其中 VAL 可以包含正则表达式反向引用($N%N),这些反向引用将被展开。您可以多次使用此标志来设置多个变量。
  • forbidden|F”(强制 URL 被forbidden)
    这强制当前 URL 被禁止 - 它立即发送 403(禁止)的 HTTP 响应。将此标志与适当的 RewriteConds 结合使用以有条件地阻止某些 URL。
  • gone|G”(强制 URL 被gone)
    这会强制当前 URL 消失 - 它会立即发送一个 410(已消失)的 HTTP 响应。使用此标志将不再存在的页面标记为已消失。
  • 'host|H=主机'(将重写应用于机)
    虚拟主机将被重写,而不是 URL。
  • 'last|L'(后一条规则)
    在此处停止重写过程,并且不要应用任何其他重写规则。这对应于 Perl 中的 last 命令或 C 中的 break 命令。使用此标志可防止当前重写的 URL 被后续规则进一步重写。例如,使用它将根路径 URL('/')重写为一个真实的 URL,例如,'/e/www/'。
  • 'next|N'(一轮)
    重新运行重写过程(从第一条重写规则重新开始)。这一次,要匹配的 URL 不再是原始 URL,而是上一条重写规则返回的 URL。这对应于 Perl 中的 next 命令或 C 中的 continue 命令。使用此标志重新启动重写过程 - 立即转到循环的顶部。
    小心不要创建无限循环!
  • 'nocase|NC'(no case)
    这会使模式不区分大小写,在将模式与当前 URL 匹配时忽略“A-Z”和“a-z”之间的差异。
  • 'noescape|NE'(输出的 URI 义)
    此标志可防止重写阀将通常的 URI 转义规则应用于重写的结果。通常,特殊字符(例如“%”、“$”、“;”等)将转义为它们的十六进制等效项(分别为“%25”、“%24”和“%3B”);此标志可防止这种情况发生。这允许百分号符号出现在输出中,如下所示
    RewriteRule /foo/(.*) /bar?arg=P1\%3d$1 [R,NE]
    它会将“/foo/zed”转换为对“/bar?arg=P1=zed”的安全请求。
  • 'qsappend|QSA'(符串加)
    此标志强制重写引擎将替换字符串的查询字符串部分附加到现有字符串,而不是替换它。当您想通过重写规则向查询字符串添加更多数据时,请使用此标志。
  • 'redirect|R [=代码]'(强制 定向
    使用 http://thishost[:thisport]/(这会使新 URL 成为一个 URI)作为替换的前缀,以强制进行外部重定向。如果没有给出代码,则会返回一个 302(已找到,以前为暂时移动)的 HTTP 响应。如果您想在 300-399 范围内使用其他响应代码,只需指定相应的数字或使用以下符号名称之一:temp(默认)、permanentseeother。将此标志用于规则,以规范化 URL 并将其返回给客户端 - 将“/~”转换为“/u/”,或始终将斜杠附加到 /u/user 等。
    注意:当您使用此标志时,请确保替换字段是有效的 URL!否则,您将重定向到无效位置。请记住,此标志本身只会将 http://thishost[:thisport]/ 前置到 URL,并且重写将继续。通常,您希望在此处停止重写,并立即重定向。要停止重写,您应该添加“L”标志。
  • skip|S=num”(skip 下一条规则)
    如果当前规则匹配,此标志强制重写引擎跳过序列中的下一条 num 规则。使用此标志可以创建伪 if-then-else 结构:then 子句的最后一条规则变为 skip=N,其中 N 是 else 子句中的规则数。(这不同于“chain|C”标志!)
  • type|T=MIME-type”(强制 MIME type)
    强制目标文件的 MIME 类型为 MIME-type。这可用于基于某些条件设置内容类型。例如,以下代码段允许 .php 文件在使用 .phps 扩展名调用时由 mod_php 显示
    RewriteRule ^(.+\.php)s$ $1 [T=application/x-httpd-php-source]
  • valveSkip|VS”(跳过阀门)
    此标志可用于设置阀门的条件执行。当设置标志并且规则匹配时,重写阀门将跳过 Catalina 管道中的下一个阀门。如果重写阀门是管道中的最后一个阀门,则将忽略该标志,并且将调用容器基本阀门。如果发生重写,则该标志将没有任何效果。