记一次配置rewrite和return的经历

前言与需求

自动电商平台归属了大数据研究院之后,我又恢复了那个“把nginx当成爸爸”的日子。开发不断地提出了的要求,我一样一样的疲命应付,并且在应付后记录下来,就怕以后再遇到类似的问题。

这次的需求是一个跳转,满足某个条件之后把“http://dvlshop.lechange.com/index.php/wap/?client_id=lc_mall_m&redirect_uri=https%3A%2F%2Fdvlshop.lechange.com%2Fopenapi%2Ftrustlogin_api%2Fparse%2Fwap_trustlogin_lecheng%2Fcallback&response_type=code&scope=read&state=http%3A%2F%2Fdvlshop.lechange.com%2Findex.php%2Fwap&user=token%2Flcid_9f9lmo2u6i7hkl6t6eaodn2blmg5jbsg&expire=1514191636&source_type=lc_app&nonce=cdizHO6uvSx5JK79Kmtz5RBpSi0ROhpF&signature=VeCceYCWDE6BZjIdni/68YCmhqc=%27 ”
改成
“http://dvlshop.lechange.com/index.php/wap/?client_id=lc_mall_m&redirect_uri=https%3A%2F%2Fdvlshop.lechange.com%2Fopenapi%2Ftrustlogin_api%2Fparse%2Fwap_trustlogin_lecheng%2Fcallback&=code&scope=read&state=http%3A%2F%2Fdvlshop.lechange.com%2Findex.php%2Fwap&user=token%2Flcid_9f9lmo2u6i7hkl6t6eaodn2blmg5jbsg&expire=1514191636&source_type=lc_app&nonce=cdizHO6uvSx5JK79Kmtz5RBpSi0ROhpF&signature=VeCceYCWDE6BZjIdni/68YCmhqc=%27”

具体条件是:

  1. 先判断是否有source_type=lc_app;
  2. 再判断是否有response_type;
  3. 如果以上两个都满足,将“response_type”改成“+auto+”;

各位看官,我理解你们此时不想继续看下去的心情,其实我当初看着那么一大坨uri心里也直犯闹,但是没办法,“食君之禄,分君之忧”,我只能耐着性子一个一个的拆开,还别说,拆开的话就清晰许多了,如下:
http://dvlshop.lechange.com/index.php/wap/?
client_id=lc_mall_m&
redirect_uri=https%3A%2F%2Fdvlshop.lechange.com%2Fopenapi%2Ftrustlogin_api%2Fparse%2Fwap_trustlogin_lecheng%2Fcallback&
response_type=code& #满足条件的话把这个改成+auto+
scope=read&
state=http%3A%2F%2Fdvlshop.lechange.com%2Findex.php%2Fwap&
user=token%2Flcid_9f9lmo2u6i7hkl6t6eaodn2blmg5jbsg&
expire=1514191636&
source_type=lc_app&
nonce=cdizHO6uvSx5JK79Kmtz5RBpSi0ROhpF&signature=VeCceYCWDE6BZjIdni/68YCmhqc=%27

开始操作

针对这次需求我的计划是这样的:把原地址看成”$1+ response_type +$2”这样的一个样式,确定$1和$2,然后rewrite成”$1+ +auto+ +$2”不就搞定了么? 于是乎我就凭着我那二把刀的nginx技术开始动手。折腾了大约半个小时,拿出来这样一个配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
location ~ .*\.php.*
{
include php_fcgi.conf;
include pathinfo.conf;
set $flag "0";
if ( $request_uri ~ "source_type=lc_app" ) {
set $flag "1";
}
if ( $request_uri ~ "(.*)response_type(.*)" ){
set $Flag "$flag$flag";
set $id $1;
set $query $2;
}
if ($Flag = "11"){ #注意这个地方是11
set $flag "0";
rewrite ^.*$ http://dvlshop.lechange.com/index.php/wap/$id$query last; #前面那一段是写死的
}
}

但是很不幸,nginx -s reload之后的结果是“$1+$2+$1+ response_type +$2”的格式(地址太长太恶心了,我就不写了)。

然后在arstercz大神的指点下,把那句rewrite改成了return 301 http://dvlshop.lechange.com/index.php/wap/?$id$query;。就达到了效果。

原因确定

后来追寻原因,原来是: rewrite后面接的$uri不需要$args,因为$args会被自动带过来。而return的则会丢失$args,需要手动补上$args。而我上面的$1,$2恰巧就是$args,所以用rewrite的话就会重复。举个例子,比如请求「http://localhost/?a=1」想被 301 到「https://localhost/?a=1?a=1」,要么

1
2
3
4
server {
listen 80;
rewrite / https://$host$uri permanent;
}

要么就

1
2
3
4
server {
listen 80;
return 301 https://$host$request_uri;
}

补充说明

这里补充一下uri、request_uri、document_uri之间的区别:

  • $request_uri: /stat.php?id=1585378&web_id=1585378
  • $uri: /stat.php (不带?后面)
  • $document_uri: /stat.php (与uri完全相同)

注意!$uri$document_uri同是不带参数的,但是是解码之后请求的路径,而$request_uri是没有解码的完整uri。这样就有一个问题,就是$uri是解码的,这样就可能包含换行符,CRLF就会注入漏洞。详情可见 https://www.leavesongs.com/PENETRATION/nginx-insecure-configuration.html

所以说,不要偷懒少打几个字母,最好都要用$request_uri

参考资料

https://www.cnblogs.com/bigberg/p/7715020.html
https://blog.csdn.net/yxl0011/article/details/72818409
https://blog.csdn.net/aliencsdn/article/details/54668552

抱光妹

感谢您请我喝咖啡~O(∩_∩)O,如果要联系请直接发我邮箱chenx1242@163.com,我会回复你的
-------------本文结束感谢您的阅读-------------