靜態(tài)文件下載權(quán)限控制

在學(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)資源的地址

image.png

擔(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ù)器資源沈撞。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末锚贱,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子关串,更是在濱河造成了極大的恐慌拧廊,老刑警劉巖监徘,帶你破解...
    沈念sama閱讀 211,123評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異吧碾,居然都是意外死亡凰盔,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門倦春,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)户敬,“玉大人,你說(shuō)我怎么就攤上這事睁本∧蚵” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 156,723評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵呢堰,是天一觀的道長(zhǎng)抄瑟。 經(jīng)常有香客問(wèn)我,道長(zhǎng)枉疼,這世上最難降的妖魔是什么皮假? 我笑而不...
    開(kāi)封第一講書人閱讀 56,357評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮骂维,結(jié)果婚禮上惹资,老公的妹妹穿的比我還像新娘。我一直安慰自己航闺,他們只是感情好褪测,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,412評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著潦刃,像睡著了一般侮措。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上福铅,一...
    開(kāi)封第一講書人閱讀 49,760評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音项阴,去河邊找鬼滑黔。 笑死,一個(gè)胖子當(dāng)著我的面吹牛环揽,可吹牛的內(nèi)容都是我干的略荡。 我是一名探鬼主播,決...
    沈念sama閱讀 38,904評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼歉胶,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼汛兜!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起通今,我...
    開(kāi)封第一講書人閱讀 37,672評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤粥谬,失蹤者是張志新(化名)和其女友劉穎肛根,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體漏策,經(jīng)...
    沈念sama閱讀 44,118評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡派哲,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,456評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了掺喻。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片芭届。...
    茶點(diǎn)故事閱讀 38,599評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖感耙,靈堂內(nèi)的尸體忽然破棺而出褂乍,到底是詐尸還是另有隱情,我是刑警寧澤即硼,帶...
    沈念sama閱讀 34,264評(píng)論 4 328
  • 正文 年R本政府宣布逃片,位于F島的核電站,受9級(jí)特大地震影響谦絮,放射性物質(zhì)發(fā)生泄漏题诵。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,857評(píng)論 3 312
  • 文/蒙蒙 一层皱、第九天 我趴在偏房一處隱蔽的房頂上張望性锭。 院中可真熱鬧,春花似錦叫胖、人聲如沸草冈。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,731評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)怎棱。三九已至,卻和暖如春绷跑,著一層夾襖步出監(jiān)牢的瞬間拳恋,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,956評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工砸捏, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留谬运,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,286評(píng)論 2 360
  • 正文 我出身青樓垦藏,卻偏偏與公主長(zhǎng)得像梆暖,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子掂骏,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,465評(píng)論 2 348