在學(xué)習(xí)權(quán)限管理時(shí)芭梯,想起服務(wù)器上的靜態(tài)資源也需要進(jìn)行權(quán)限管理酱畅,否則,只要知道資源路徑君旦,任何人都可以訪問(wèn)下載(windows可以通過(guò)設(shè)置文件夾權(quán)限澎办,設(shè)置用戶名和密碼嘲碱,linux這部分還不太了解,大約也是可以的)局蚀,有關(guān)這個(gè)問(wèn)題麦锯,參考以下博客:
https://blog.csdn.net/w6299702/article/details/45576957
測(cè)試過(guò)程:
一個(gè)簡(jiǎn)單的下載api:
from flask import Flask, make_response
app = Flask(__name__)
@app.route('/download', methods=['GET'])
def download():
response = make_response('ok', 200)
response.headers['X-Accel-Redirect'] = 'static/test.jpg'
return response
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5001, debug=True)
服務(wù)器上部署此應(yīng)用,同時(shí)用nginx做反向代理
nginx配置如下:
server {
listen 8090;
server_name 0.0.0.0;
location / {
proxy_pass http://127.0.0.1:5001/;
}
}
瀏覽器訪問(wèn):http://192.168.**.**:8090/download
服務(wù)端響應(yīng)如下琅绅,可以看到第一條請(qǐng)求響應(yīng)成功扶欣,由于response Header中加了X-Accel-Redirect,該請(qǐng)求并沒(méi)有直接返回信息千扶,而是進(jìn)行了重定向至資源路徑料祠。
在攔截的請(qǐng)求中沒(méi)有暴露靜態(tài)資源的地址
擔(dān)心博客會(huì)失效,將全文貼出來(lái)澎羞,若有侵權(quán)术陶,請(qǐng)作者直接聯(lián)系刪除,以下為原文內(nèi)容煤痕,另外,沒(méi)有找到原作者的鏈接接谨。摆碉。。
要對(duì)下載的權(quán)限進(jìn)行精確的控制(防止盜鏈脓豪,防止迅雷吸血巷帝,下載扣除積分等虛擬貨幣),以前接觸的方法有幾種:
1扫夜、通過(guò)rewrite不斷地更改下載文件的url楞泼,并插入很多無(wú)意義的字符;
2笤闯、驗(yàn)證下載鏈接的來(lái)路堕阔,或者cookie;
3颗味、通過(guò)服務(wù)器端程序(例如一個(gè)php文件)超陆,open文件,讀取內(nèi)容然后返回給客戶端浦马。
第一種方法很笨时呀,而且吃力不討好;
第二種方法很容易破解晶默,因?yàn)閞eferer和cookie都是客戶端發(fā)出的谨娜,能夠方便地偽造,而且迅雷對(duì)此已經(jīng)是輕車熟路磺陡;
第三種方法是可行的有效的趴梢,所有的文件都經(jīng)過(guò)一個(gè)程序讀取并發(fā)送漠畜,在讀取之前可以有效的驗(yàn)證權(quán)限,但是下載過(guò)程中始終要占用一個(gè)cgi線程垢油,而且一般cgi語(yǔ)言的IO性能都不好盆驹,速度很慢,占用了服務(wù)器的大量資源滩愁,導(dǎo)致總體效率極其低下躯喇,難以大規(guī)模運(yùn)用。
為此我研究了一下csdn下載頻道的實(shí)現(xiàn)機(jī)制硝枉。
csdn下載頻道能夠有效的驗(yàn)證權(quán)限廉丽,扣除積分,而且不排斥迅雷等下載客戶端妻味,同一個(gè)用戶下載同一個(gè)文件也不會(huì)重復(fù)扣除積分正压,而且下載時(shí)始終沒(méi)有暴露文件的真實(shí)地址,同一個(gè)下載URL到了別的地方也完全不可用责球,可以說(shuō)是實(shí)現(xiàn)得比較理想的焦履。
我選擇了一個(gè)文件進(jìn)行測(cè)試,下載的url是: http://dldx.csdn.net/fd.php?i=573624740728082&s=4fc2353ca769a0ebd9237b6f98791679
這個(gè)url向文件存儲(chǔ)服務(wù)器上的fd.php文件發(fā)送了兩個(gè)經(jīng)過(guò)加密的參數(shù)雏逾,里面應(yīng)該包含有用戶登錄信息(用戶ID和sid)和目標(biāo)文件的ID號(hào)嘉裤。
用迅雷下載這個(gè)文件,截獲返回的頭信息:
Host: dldx.csdn.net
Pragma: no-cache
Range: bytes=0-
Referer: http://d.download.csdn.net/down/2474072/waf9898
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; )
HTTP/1.1 206 Partial Content
Server: nginx/0.7.65
Date: Tue, 22 Jun 2010 07:08:21 GMT
Content-Type: “application/octet-stream; charset=utf-8″
Content-Length: 667747
Last-Modified: Mon, 21 Jun 2010 23:45:02 GMT
Connection: keep-alive
Content-Disposition: attachment; filename=”DNF%E6%82%A0%E6%82%A05%5B1%5D.7.rar”
Expires: 0
Cache-Control: must-revalidate, post-check=0, pre-check=0
Content-Range: bytes 0-667746/667747
這里面始終沒(méi)有暴露目標(biāo)文件的真實(shí)路徑栖博,不是一般下載系統(tǒng)所使用的header重定向的方式屑宠。而且有一個(gè)重命名的信號(hào)。服務(wù)器使用的程序是nginx/0.7.65仇让。
根據(jù)這些信息典奉,在google搜索到這篇文章:http://kovyrin.net/2006/11/01/nginx-x-accel-redirect-php-rails/
顯然,csdn就是使用了文中所說(shuō)的nginx X-Accel-Redirect丧叽。
解釋一下整個(gè)過(guò)程:
步驟1卫玖,客戶端請(qǐng)求http://dldx.csdn.net/fd.php ,并傳遞相關(guān)信息踊淳;
步驟2骇笔,fd.php根據(jù)所傳遞的信息判斷出訪問(wèn)者的身份和所請(qǐng)求的資源,然后應(yīng)該驗(yàn)證了客戶端的IP嚣崭,進(jìn)一步判斷其權(quán)限笨触。如果這個(gè)客戶端有權(quán)下載 此文件,則在HTTP header加入X-Accel-Redirect: (文件的真實(shí)路徑)雹舀,并加上head Content-Type和Content-Disposition:芦劣;
步驟3,nginx得到fd.php的回應(yīng)后發(fā)現(xiàn)帶有X-Accel-Redirect的header说榆,那么根據(jù)這個(gè)頭記錄的路徑信息打開(kāi)目標(biāo)文件虚吟;
步驟4寸认,nginx把打開(kāi)文件的內(nèi)容返回給客戶端。
這樣所有的權(quán)限檢查和積分扣除等操作都可以在步驟2內(nèi)完成串慰,而且fd.php返回帶X-Accel-Redirect的頭后偏塞,其執(zhí)行已經(jīng)終止,剩下 的傳輸文件的工作由nginx 來(lái)接管邦鲫,同時(shí)X-Accel-Redirect頭的信息被nginx刪除灸叼,不會(huì)返回給客戶端,也就不會(huì)暴露(實(shí)際上可以把目標(biāo)文件存儲(chǔ)在不能經(jīng)由web訪 問(wèn)的目錄)庆捺,并且由于nginx在打開(kāi)靜態(tài)文件上使用了 sendfile()古今,其IO效率非常高,比php的IO要快上N++倍滔以。
這是一種優(yōu)雅捉腥,有效,高效的實(shí)現(xiàn)方案你画。
因?yàn)闆](méi)有架設(shè)過(guò)nginx服務(wù)器抵碟,我希望能在apache實(shí)現(xiàn)這個(gè)功能,于是查找了一下有沒(méi)有類似的mod坏匪,果然查找到了一個(gè) mod_xsendfile:http://tn123.ath.cx/mod_xsendfile/ 立磁,其實(shí)現(xiàn)機(jī)制與nginx的X-Accel-Redirect基本相同。
下載之后在本機(jī)測(cè)試剥槐。
1、加載mod_xsendfile宪摧。將文件 mod_xsendfile.so 移動(dòng)到 apache/modules 目錄下粒竖,將以下內(nèi)容添加到httpd.conf中
LoadModule xsendfile_module modules/mod_xsendfile.so
XSendFile On
XSendFileAllowAbove On
2、使用PHP調(diào)用X-sendfile几于。代碼如下:
接收_GET數(shù)據(jù)并解密蕊苗;
驗(yàn)證uid、sid沿彭、文件id朽砰;
如果通過(guò)驗(yàn)證:
{
扣除積分、計(jì)數(shù)統(tǒng)計(jì)等操作喉刘;
header('Content-Type:(目標(biāo)文件類型)');
header('Content-Disposition: attachment; filename="(希望客戶下載到的文件名)"');
header('X-Sendfile:(目標(biāo)文件真實(shí)路徑瞧柔,使用絕對(duì)路徑,例如"E:/www/dl/test.rar'睦裳,此路徑可以是web無(wú)法訪問(wèn)的目錄")');
exit;
}
如果不通過(guò):
{
給客戶端返回一個(gè)提示性的html文件造锅;
}
?>
3、構(gòu)造下載url廉邑,用迅雷成功下載哥蔚;破壞驗(yàn)證條件(比如改變客戶端IP)之后倒谷,迅雷只能下載到提示錯(cuò)誤的文件。
實(shí)際應(yīng)用中可以采用以下具體方案:
1糙箍、把所有的目標(biāo)文件都存儲(chǔ)在服務(wù)器B渤愁,此服務(wù)器不需要數(shù)據(jù)庫(kù),而且通過(guò)web只能訪問(wèn)到某入口文件(比如http://dldx.csdn.net/fd.php)深夯,在這個(gè)文件中配合apache實(shí)現(xiàn)X-Sendfile抖格;
2、 網(wǎng)站文件(php和html)塌西,以及數(shù)據(jù)庫(kù)運(yùn)行在服務(wù)器A(當(dāng)然數(shù)據(jù)庫(kù)也可以另設(shè)服務(wù)器)他挎,此服務(wù)器負(fù)責(zé)構(gòu)造類似于 http://dldx.csdn.net/fd.php?i=573624740728082&s=4fc2353ca769a0ebd9237b6f98791679 的url;
3捡需、服務(wù)器B接到以上URL以后办桨,分析客戶端IP,然后遠(yuǎn)程連接服務(wù)器A的數(shù)據(jù)庫(kù)站辉,把uid呢撞,sid,文件id饰剥,客戶端IP進(jìn)行匹配分析殊霞,通過(guò)則扣除積分放行下載,否則提示錯(cuò)誤汰蓉。
此方案最終就能夠?qū)崿F(xiàn)以下目的:
1绷蹲、任何方式都無(wú)法直接通過(guò)web訪問(wèn)到目標(biāo)文件,迅雷也沒(méi)有辦法顾孽;
2祝钢、類似于 http://dldx.csdn.net/fd.php?i=573624740728082&s=4fc2353ca769a0ebd9237b6f98791679 的URL沒(méi)有通用性,只能特定的用戶在特定的IP訪問(wèn)特定的文件若厚,迅雷即使把這個(gè)URL存儲(chǔ)起來(lái)拦英,也是沒(méi)有辦法吸血的(只能下載到提示錯(cuò)誤的文件);
3测秸、文件存儲(chǔ)和數(shù)據(jù)庫(kù)兩臺(tái)服務(wù)器干凈的分割疤估,便于維護(hù);
4霎冯、速度和效果都很完美铃拇,不會(huì)產(chǎn)生驗(yàn)證差錯(cuò),也不會(huì)過(guò)多占用服務(wù)器資源沈撞。