linux's docs

22.3.0: 访问控制-rewrite


0. 环境介绍

# 虚机配置
<VirtualHost *:80>
        ServerName www.example.com
        DocumentRoot "/var/www/html"
        CustomLog "logs/server0_vhost_log" combined
        <Directory "/var/www/html">
                <RequireAll>
                        Require all granted
                </RequireAll>
        </Directory>
</VirtualHost>

1. 加载重定向模块

# 编辑主配文件,加载rewrite模块
LoadModule rewrite_module modules/mod_rewrite.so

# 重载服务配置,检查模块加载情况
apachectl graceful
apachectl -M |grep rewrite
 rewrite_module (shared)

2. 重定向配置

# 虚拟主机中重定向配置
<IfModule mod_rewrite.c>
        RewriteEngine on
        RewriteCond %{HTTP_HOST} ^bbs.example.com$
        RewriteRule ^/(.*)$ http://www.example.com/$1 [R=301,L]
</IfModule>

# 重载服务配置,检查重定向结果
apachectl graceful
curl -x localhost:80 bbs.example.com -I
HTTP/1.1 301 Moved Permanently
Date: Wed, 25 May 2016 14:26:12 GMT
Server: Apache/2.4.20 (Unix) PHP/5.6.21
Location: http://www.example.com/
Content-Type: text/html; charset=iso-8859-1

3. 理论知识漫谈

RewriteRule的正则知识
官网链接: http://httpd.apache.org/docs/2.4/rewrite/intro.html

1) 什么是$1,$2和%1...?

  • "()" - 在RewriteCond中代表%1......
  • "()" - 在RewriteRule中代表$1......

2) RewriteRule语法

  1. Pattern - 请求的URL(http://hostname:port之后的部分)匹配的规则
  2. substitution - 匹配到的URL部分发送到此处重置位置
    • 文件系统绝对路径
      RewriteRule "^/games" "/usr/local/games/web"
    • web路径
      RewriteRule "^/foo$" "/bar"
      相当于$DocumentRoot/bar
    • URL绝对路径
      RewriteRule "^/product/view$" "http://site2.example.com/seeproduct.html" [R]
  3. [flags] - 影响rewrite请求的选项

3) RewriteRule的执行逻辑

## 访问bbs.example.com时,根据后面是纯数字或纯字母做出反应
# RewriteEngine on
# RewriteCond %{HTTP_HOST} ^(http://)?bbs.example.com
# RewriteRule ^/([0-9]+)$ http://www.example.com/first$1
# RewriteRule ^/([0-9]+)$ http://www.example.com/second$1
# RewriteRule ^/([a-z]+)$ http://www.example.com/third$1
# RewriteRule ^/(.*)$ http://www.example.com/fourth$1

## 通过下面的三次测试可以得出以下结论
## 当RewriteCond条件匹配到的情况下
# 匹配到的位置最上面的rule生效
## 当RewriteCond条件不匹配的情况下
# 跳过第一条rule,匹配到的最上面的rule生效

# RewriteCond匹配,三条rule匹配,上面的生效
curl -x localhost:80 bbs.example.com/11 -I
HTTP/1.1 302 Found
Date: Thu, 26 May 2016 14:25:29 GMT
Server: Apache/2.4.20 (Unix) PHP/5.6.21
Location: http://www.example.com/first11
Content-Type: text/html; charset=iso-8859-1

# RewriteCond匹配,若只有一条rule匹配,该rule生效
curl -x localhost:80 bbs.example.com/aa1 -I
HTTP/1.1 302 Found
Date: Thu, 26 May 2016 15:33:50 GMT
Server: Apache/2.4.20 (Unix) PHP/5.6.21
Location: http://www.example.com/fourthaa1
Content-Type: text/html; charset=iso-8859-1


# RewriteCond不匹配时,因为第4条rule匹配到,该rule生效
# 但当我测试将第4条rule换去第一条,未生效
curl -x localhost:80 www.baidu.com -I
HTTP/1.1 302 Found
Date: Thu, 26 May 2016 15:26:00 GMT
Server: Apache/2.4.20 (Unix) PHP/5.6.21
Location: http://www.example.com/fourth
Content-Type: text/html; charset=iso-8859-1

RewriteCond语法

  1. teststring - request的相关变量
  2. dondition - 正则匹配字符串
  3. [flags] - NC(忽略大小写), OR(或), NV(No Vary)
  4. 可有1个或多个RewriteCond
  5. 当有多个RewriteCond时,需要满足所有条件

4. 实践扩展

扩展1 - 用rewrite来匹配特定文件类型,并禁止访问

<IfModule mod_rewrite.c>
RewriteEngine on
RewriteCond %{REQUEST_URI} ^.*/tmp/* [NC]
RewriteRule .* - [F]
</IfModule>
## 条件: 当request请求的url中包含/tmp/时
## NC: 判断条件不区分大小写
## RewriteRule中的"-"代表什么都不做
## F: 拒绝访问

扩展2 - 用rewrite匹配特定useragent,并禁止访问

user_agent是什么?
user_agent就是访问网络的工具(浏览器),因为user是通过浏览器和server沟通的,所以浏览器就是user的agent
目前,一方面浏览器基本都通过给server发送一条user agent字符串,来告诉server自己的访问信息;另外一方面,搜索引擎(google、baidu)在放出网络爬虫来收集网站link时,网络爬虫也会发送user agent字符串来表明自己的身份。

user_agent string
此图来自网络http://whatsmyuseragent.com/WhatsAUserAgent

## rewrite写法
<IfModule mod_rewrite.c>
    RewriteEngine on
    RewriteCond %{HTTP_USER_AGENT}  ^*Firefox/4.0* [NC,OR]
    RewriteCond %{HTTP_USER_AGENT}  ^*Tomato Bot/1.0* [NC]
    RewriteRule  .*  -  [F]
</IfModule>
## 不推荐用rewrite到404,会产生死循环

扩展3 - 死循环相关知识

## 原意是把所有访问请求rewrite到/111/$1
RewriteRule ^(.*) /111/$1 [R,L]
## 但当访问类似于www.111.com/111,它就会无限循环在网址后面增加111

## 为了避免死循环产生,可以按如下方法处理
RewriteCond   %{REQUEST_URI} !^/111
RewriteRule ^(.*) /111/$1 [R,L]

扩展4 - 多RewriteRule对应RewriteCond

## 使用rewrite flags - [E=var:varvalue]
RewriteCond %{HTTP_HOST} ^(www\.)?([a-z0-9-]+)\.example\.com [NC]
RewriteRule .? - [E=Wa:%1,E=Wb:%2]
RewriteRule ^(.*?)-([a-z]+) %{ENV:Wb}/$1.%{ENV:Wb} [L]
RewriteRule ^(.*?)-([0-9]+)([a-z]) %{ENV:Wb}/$1$3.$2 [L]
## 将RewriteCond的%1和%2赋值给变量
## rewrite flag [L]含义是last,代表此规则是最后一条规则;

扩展5 - 用rewrite flag [S]来改变扩展5中的逻辑处理方式

## 当访问非bbs.example.com时,跳过3行Rule
## 当访问bbs.example.com时,因为不匹配条件,跳至第二行rule
## 根据第二行rule规则,跳过第三行,执行第四行规则
RewriteEngine on
RewriteCond %{HTTP_HOST} !bbs.example.com
RewriteRule .? - [S=3]
RewriteRule .? - [S=1]
RewriteRule ^/(.*)$ http://bbs.example.com/$1s
RewriteRule ^/(.*)$ http://bbs.example.com/$1f

## 验证结果
# curl -x localhost:80 bbs.example.com -I
HTTP/1.1 302 Found
Date: Thu, 26 May 2016 11:03:51 GMT
Server: Apache/2.4.20 (Unix) PHP/5.6.21
Location: http://bbs.example.com/f
Content-Type: text/html; charset=iso-8859-1

Contents