在web應用中,經常會有文件須要下載。若是這些文件是很是私密的,直接用web服務器下載,就不能檢查文件的下載權限。以往遇到這種須要權限的狀況,都是用程序語言斷定權限後,使用程序語言來讀取文件並輸出,這樣就能解決權限問題。可是使用程序語言來讀取文件又帶來了效率上的問題,若是文件體積比較大或者下載併發數比較大,服務器很快就不堪重負。
基於這種狀況,web服務器軟件提供了相應的解決辦法:使用一個response header來控制下載。目前squid、apache、lighttpd、nginx等http server都有支持這種方式,可是他們的response header的名字都不同:
nginx: X-Accel-Redirect
squid: X-Accelerator-Vary
apache: X-Sendfile
lighttpd: X-Sendfile/X-LIGHTTPD-send-file
用response header控制下載的原理都大同小異:
當客戶端發起請求下載某個文件時,由於並無X-Accel-Redirect頭,web服務器並不會馬上就把文件輸出給客戶端;而是將這個請求交給後端的程序語言,程序語言驗證認爲該客戶端能夠下載這個文件,就寫出相應的X-Accel-Redirect頭並結束處理;X-Accel-Redirect頭返回時通過前端的web服務器,web服務器檢查到這個頭以後,才把文件輸出到客戶端。
那麼,若是客戶端僞造一個X-Accel-Redirect頭來讀取呢?固然也是不能下載的,由於web服務器只認識後端發來的X-Accel-Redirect頭,客戶端發來的不算。
因而下面就用nginx來實現上述的這個流程:
一、改變目錄權限,客戶端發起請求時,將這個目錄的請求都交給後端
location /mp3/ {
alias /data/html/mp3/;
internal;
error_page 403 =200 @backend ;
}
location @backend {
proxy_pass http://www.test.com;
}
這樣,用戶訪問如http://www.test.com/mp3/1.mp3這樣的地址時,將不能下載文件,nginx會把請求交給後端服務器。
二、在後端服務器配置一個rewrite
rewrite "^/mp3/(.*)\.mp3$" /read_file.do?id=$1 last;
這個rewrite的目的是把請求http://www.
test.com/mp3/1.mp3指向到一個php程序語言上,由程序語言處理。
三、寫一個java程序判斷權限
httpResponse.setHeader("Content-Disposition", "attachment; filename=\""+filename+"\"");
httpResponse.setHeader("Content-Type","application/octet-stream");
httpResponse.setHeader("X-Accel-Redirect","/mp3/"+resource.get("res_url"));
//給nginx返回實際文件存在的地址
這樣,配置就完成了。