NGINX?權(quán)限控制文件預(yù)覽和下載的實現(xiàn)原理
@date: 2020-07-31 06:00
基于 Nginx + Java(SpringBoot) 實現(xiàn)帶權(quán)限驗證的靜態(tài)文件服務(wù)器,支持文件下載、PDF預(yù)覽和圖片預(yù)覽。
需要注意的是,無需權(quán)限判斷的圖片不建議使用此方法,大量的圖片訪問會增加后臺服務(wù)器的處理壓力。
一、實現(xiàn)原理
本質(zhì)上是使用了X-Sendfile功能來實現(xiàn),X-Sendfile 是一種將文件下載請求重定向到Web 服務(wù)器處理的機制,該Web服務(wù)器只需負責(zé)處理請求(例如權(quán)限驗證),而無需執(zhí)行讀取文件并發(fā)送給用戶的任務(wù)。
X-Sendfile可顯著提高后臺服務(wù)器的性能,消除了后端程序既要讀文件又要處理發(fā)送的壓力,尤其是處理大文件下載的情形下!
Nginx也具有此功能,但實現(xiàn)方式略有不同。在Nginx中,此功能稱為X-Accel-Redirect。
用戶請求文件,權(quán)限控制時序圖:

二、實現(xiàn)步驟
1. NGINX配置
1、靜態(tài)文件通過
file_server訪問,會被設(shè)置為internal,即只能內(nèi)部訪問不允許外部直接訪問。
2、所有靜態(tài)資源請求均被重定向到Java后臺,經(jīng)過權(quán)限驗證后才能訪問。
# 文件下載服務(wù)
location ^~ /file_server {
# 內(nèi)部請求(即一次請求的Nginx內(nèi)部請求),禁止外部訪問,重要。
internal;
# 文件路徑
alias U:/file/private/;
limit_rate 200k;
# 瀏覽器訪問返回200,然后轉(zhuǎn)由后臺處理
error_page 404 =200 @backend;
}
# 文件下載鑒權(quán)
location @backend {
# 去掉訪問路徑中的 /file_server/,然后定義新的請求地址。
rewrite ^/file_server/(.*)$ /uecom/attach/$1 break;
# 這里的url后面不可以再拼接地址
proxy_pass http://192.168.12.68:9023;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}2. JAVA SPRINGBOOT 后臺權(quán)限驗證
用戶請求只需要傳遞附件參數(shù)和身份token,不再需要傳遞服務(wù)器的真實路徑。例如:
http://192.168.12.68:9022/file_server/preview_file?id=13e9d1887b46455a96842c503c2434cb&access_token=eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ3ZWJfMSIsImV4cCI6MTU5NjE1NzkxOH0.eR4yYlDjIMXZmNNTq2gdghkYhw6I30NgZlvuPUmRoyk
2.1 權(quán)限校驗文件下載
response.setHeader("X-Accel-Redirect", "/file_server" + attach.getAttachPath()); ,重點是X-Accel-Redirect配置返回服務(wù)器文件的真實路徑,該路徑返回后由Nginx內(nèi)部請求處理,不會暴露給請求用戶。
/**
* @describe 使用token鑒權(quán)的文件下載
* @author momo
* @date 2020-7-30 13:44
* @param id 文件唯一編碼 uuid
* @return void
*/
@GetMapping("/download_file")
public void downloadFile(@NotNull String id) throws IOException {
HttpServletResponse response = super.getHttpServletResponse();
// 通過唯一編碼查詢附件
Attach attach = attachService.getById(id);
if (attach == null) {
// 附件不存在,跳轉(zhuǎn)404
this.errorPage(404);
return;
}
// 從訪問token中獲取用戶id。 token也可以通過 參數(shù) access_token 傳遞
Integer userId = UserKit.getUserId();
if (userId == null || ) {
// 無權(quán)限訪問,跳轉(zhuǎn)403
this.errorPage(403);
return;
}
// 已被授權(quán)訪問
// 文件下載
response.setHeader("Content-Disposition", "attachment; filename=\"" + new String(attach.getAttachName().getBytes("GBK"), "iso-8859-1") + "\"");
// 文件以二進制流傳輸
response.setHeader("Content-Type", "application/octet-stream;charset=utf-8");
// 返回真實文件路徑交由 Nginx 處理,保證前端無法看到真實的文件路徑。
// 這里的 "/file_server" 為 Nginx 中配置的下載服務(wù)名
response.setHeader("X-Accel-Redirect", "/file_server" + attach.getAttachPath());
// 限速,單位字節(jié),默認不限
// response.setHeader("X-Accel-Limit-Rate","1024");
// 是否使用Nginx緩存,默認yes
// response.setHeader("X-Accel-Buffering","yes");
response.setHeader("X-Accel-Charset", "utf-8");
// 禁止瀏覽器緩存
response.setHeader("Pragma", "No-cache");
response.setHeader("Cache-Control", "No-cache");
response.setHeader("Expires", "0");
}后臺可配置屬性:
Content-Type: Content-Disposition: : Accept-Ranges: Set-Cookie: Cache-Control: Expires: # 設(shè)置文件真實路徑的URI,默認void X-Accel-Redirect: void # 限制下載速度,單位字節(jié)。默認不限速度off。 X-Accel-Limit-Rate: 1024|off # 設(shè)置此連接的代理緩存,將此設(shè)置為no將允許適用于Comet和HTTP流式應(yīng)用程序的無緩沖響應(yīng)。將此設(shè)置為yes將允許響應(yīng)被緩存。默認yes。 X-Accel-Buffering: yes|no # 如果已傳輸過的文件被緩存下載,設(shè)置Nginx文件緩存過期時間,單位秒,默認不過期 off。 X-Accel-Expires: off|seconds # 設(shè)置文件字符集,默認utf-8。 X-Accel-Charset: utf-8
2.2 權(quán)限校驗文件預(yù)覽
文件下載和文件預(yù)覽本質(zhì)上沒有區(qū)別,只是response返回時告訴瀏覽器執(zhí)行下載還是執(zhí)行打開預(yù)覽。即response.setHeader("Content-Disposition", "inline; filename="xxx.pdf");, 然后修改返回數(shù)據(jù)的格式Content-Type即可。
Content-Disposition 響應(yīng)頭指示回復(fù)的內(nèi)容該以何種形式展示,是以內(nèi)聯(lián)(
inline)的形式(即網(wǎng)頁或者頁面的一部分),還是以附件(attachment)的形式下載并保存到本地。
/**
* @describe 使用token鑒權(quán)的文件預(yù)覽(支持pdf和圖片)
* @author momo
* @date 2020-7-30 13:44
* @param id 文件唯一編碼 uuid
* @return void
*/
@GetMapping("/preview_file")
public void previewFile(@NotNull String id) throws IOException {
HttpServletResponse response = super.getHttpServletResponse();
// 通過唯一編碼查詢附件
Attach attach = attachService.getById(id);
if (attach == null) {
// 附件不存在,跳轉(zhuǎn)404
this.errorPage(404);
return;
}
// 從訪問token中獲取用戶id。 token也可以通過 參數(shù) access_token 傳遞
Integer userId = UserKit.getUserId();
if (userId == null || ) {
// 無權(quán)限訪問,跳轉(zhuǎn)403
this.errorPage(403);
return;
}
// 已被授權(quán)訪問
// 文件直接顯示
response.setHeader("Content-Disposition", "inline; filename=\"" + new String(attach.getAttachName().getBytes("GBK"), "iso-8859-1") + "\"");
if ("pdf".equals(attach.getAttachType().toLowerCase())) {
// PDF
response.setHeader("Content-Type", "application/pdf;charset=utf-8");
} else {
// 圖片
response.setHeader("Content-Type", "image/*;charset=utf-8");
}
// 返回真實文件路徑交由 Nginx 處理,保證前端無法看到真實的文件路徑。
// 這里的 "/file_server" 為 Nginx 中配置的下載服務(wù)名
response.setHeader("X-Accel-Redirect", "/file_server" + attach.getAttachPath());
// 瀏覽器緩存 1 小時
response.setDateHeader("Expires", System.currentTimeMillis() + 1000 * 60 * 60);
}三、擴展功能
1. 下載統(tǒng)計、訪問日志
// 自由發(fā)揮
2. 下載限速
// 限速,單位字節(jié),默認不限
response.setHeader("X-Accel-Limit-Rate","1024");3. 防盜鏈
//判斷 Referer
String referer = request.getHeader("Referer");
if (referer == null || !referer.startsWith("https://www.itmm.wang")) {
// 無權(quán)限訪問,跳轉(zhuǎn)403
this.errorPage(403);
return;
}4. X-SENDFILE
X-Sendfile是一項功能,每個代理服務(wù)器都有自己不同的實現(xiàn)。
| Web Server | Header |
|---|---|
| Nginx | X-Accel-Redirect |
| Apache | X-Sendfile |
| Lighttpd | X-LIGHTTPD-send-file |
| Squid | X-Accelerator-Vary |
使用 X-SendFile 的缺點是你失去了對文件傳輸機制的控制。例如如果你希望在完成文件下載后執(zhí)行某些操作,比如只允許用戶下載文件一次,這個 X-Sendfile 是沒法做到的,因為請求交給了后臺,你并不知道下載是否成功。
參考資料
- X-Sendfile-從Web應(yīng)用程序有效地提供大型靜態(tài)文件
- Nginx-X-Accel-Redirect
- Nginx -XSendfile
- Content-Disposition
- Http Content-Type
到此這篇關(guān)于NGINX 權(quán)限控制文件預(yù)覽和下載的文章就介紹到這了,更多相關(guān)nginx文件預(yù)覽下載內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Nginx配置server_name為域名后無法訪問的問題解決
在Nginx的配置文件中增加服務(wù)器,其server_name設(shè)置為域名時,該網(wǎng)址不能正常訪問,所以本文給大家介紹了Nginx配置server_name為域名后無法訪問的問題解決,需要的朋友可以參考下2024-01-01
服務(wù)器報錯nginx?502?Bad?Gateway的原因及如何解決詳解
項目啟動時莫名其妙網(wǎng)站訪問不了,502 Bad Gateway,下面這篇文章主要給大家介紹了關(guān)于服務(wù)器報錯nginx?502?Bad?Gateway的原因及如何解決的相關(guān)資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下2023-06-06
Docker Nginx容器和Tomcat容器實現(xiàn)負載均衡與動靜分離操作
這篇文章主要介紹了Docker Nginx容器和Tomcat容器實現(xiàn)負載均衡與動靜分離操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-11-11

