網站首頁 編程語言 正文
@date: 2020-07-31 06:00
基于 Nginx + Java(SpringBoot) 實現帶權限驗證的靜態文件服務器,支持文件下載、PDF預覽和圖片預覽。
需要注意的是,無需權限判斷的圖片不建議使用此方法,大量的圖片訪問會增加后臺服務器的處理壓力。
一、實現原理
本質上是使用了X-Sendfile
功能來實現,X-Sendfile
?是一種將文件下載請求重定向到Web 服務器處理的機制,該Web服務器只需負責處理請求(例如權限驗證),而無需執行讀取文件并發送給用戶的任務。
X-Sendfile
可顯著提高后臺服務器的性能,消除了后端程序既要讀文件又要處理發送的壓力,尤其是處理大文件下載的情形下!
Nginx
也具有此功能,但實現方式略有不同。在Nginx中,此功能稱為X-Accel-Redirect
。
用戶請求文件,權限控制時序圖:
二、實現步驟
1. NGINX配置
1、靜態文件通過
file_server
訪問,會被設置為internal
,即只能內部訪問不允許外部直接訪問。
2、所有靜態資源請求均被重定向到Java后臺,經過權限驗證后才能訪問。
# 文件下載服務 location ^~ /file_server { # 內部請求(即一次請求的Nginx內部請求),禁止外部訪問,重要。 internal; # 文件路徑 alias U:/file/private/; limit_rate 200k; # 瀏覽器訪問返回200,然后轉由后臺處理 error_page 404 =200 @backend; } # 文件下載鑒權 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 后臺權限驗證
用戶請求只需要傳遞附件參數和身份token,不再需要傳遞服務器的真實路徑。例如:
http://192.168.12.68:9022/file_server/preview_file?id=13e9d1887b46455a96842c503c2434cb&access_token=eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ3ZWJfMSIsImV4cCI6MTU5NjE1NzkxOH0.eR4yYlDjIMXZmNNTq2gdghkYhw6I30NgZlvuPUmRoyk
2.1 權限校驗文件下載
response.setHeader("X-Accel-Redirect", "/file_server" + attach.getAttachPath());
?,重點是X-Accel-Redirect
配置返回服務器文件的真實路徑,該路徑返回后由Nginx內部請求處理,不會暴露給請求用戶。
/** * @describe 使用token鑒權的文件下載 * @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) { // 附件不存在,跳轉404 this.errorPage(404); return; } // 從訪問token中獲取用戶id。 token也可以通過 參數 access_token 傳遞 Integer userId = UserKit.getUserId(); if (userId == null || ) { // 無權限訪問,跳轉403 this.errorPage(403); return; } // 已被授權訪問 // 文件下載 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 中配置的下載服務名 response.setHeader("X-Accel-Redirect", "/file_server" + attach.getAttachPath()); // 限速,單位字節,默認不限 // 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: # 設置文件真實路徑的URI,默認void X-Accel-Redirect: void # 限制下載速度,單位字節。默認不限速度off。 X-Accel-Limit-Rate: 1024|off # 設置此連接的代理緩存,將此設置為no將允許適用于Comet和HTTP流式應用程序的無緩沖響應。將此設置為yes將允許響應被緩存。默認yes。 X-Accel-Buffering: yes|no # 如果已傳輸過的文件被緩存下載,設置Nginx文件緩存過期時間,單位秒,默認不過期 off。 X-Accel-Expires: off|seconds # 設置文件字符集,默認utf-8。 X-Accel-Charset: utf-8
2.2 權限校驗文件預覽
文件下載和文件預覽本質上沒有區別,只是response
返回時告訴瀏覽器執行下載還是執行打開預覽。即response.setHeader("Content-Disposition", "inline; filename="xxx.pdf");
, 然后修改返回數據的格式Content-Type
即可。
Content-Disposition?響應頭指示回復的內容該以何種形式展示,是以內聯(
inline
)的形式(即網頁或者頁面的一部分),還是以附件(attachment
)的形式下載并保存到本地。
/** * @describe 使用token鑒權的文件預覽(支持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) { // 附件不存在,跳轉404 this.errorPage(404); return; } // 從訪問token中獲取用戶id。 token也可以通過 參數 access_token 傳遞 Integer userId = UserKit.getUserId(); if (userId == null || ) { // 無權限訪問,跳轉403 this.errorPage(403); return; } // 已被授權訪問 // 文件直接顯示 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 中配置的下載服務名 response.setHeader("X-Accel-Redirect", "/file_server" + attach.getAttachPath()); // 瀏覽器緩存 1 小時 response.setDateHeader("Expires", System.currentTimeMillis() + 1000 * 60 * 60); }
三、擴展功能
1. 下載統計、訪問日志
// 自由發揮
2. 下載限速
// 限速,單位字節,默認不限 response.setHeader("X-Accel-Limit-Rate","1024");
3. 防盜鏈
//判斷 Referer String referer = request.getHeader("Referer"); if (referer == null || !referer.startsWith("https://www.itmm.wang")) { // 無權限訪問,跳轉403 this.errorPage(403); return; }
4. X-SENDFILE
X-Sendfile
是一項功能,每個代理服務器都有自己不同的實現。
Web Server | Header |
---|---|
Nginx | X-Accel-Redirect |
Apache | X-Sendfile |
Lighttpd | X-LIGHTTPD-send-file |
Squid | X-Accelerator-Vary |
使用?X-SendFile
?的缺點是你失去了對文件傳輸機制的控制。例如如果你希望在完成文件下載后執行某些操作,比如只允許用戶下載文件一次,這個 X-Sendfile 是沒法做到的,因為請求交給了后臺,你并不知道下載是否成功。
參考資料
原文鏈接:https://blog.csdn.net/wxb880114/article/details/122430464
相關推薦
- 2022-09-12 C++實例分析組合數的計算與排列組合的產生_C 語言
- 2022-03-20 C++靜態成員函數和this指針詳解_C 語言
- 2022-12-21 OpenHarmony實現屏幕亮度動態調節方法詳解_Android
- 2022-08-03 C++實現圖像目標區裁剪ImageCropping_C 語言
- 2022-09-30 docker部署golang?http服務時端口無法訪問的問題解決_docker
- 2022-06-17 Go語言讀取,設置Cookie及設置cookie過期方法詳解_Golang
- 2022-06-20 一文搞懂Python的hasattr()、getattr()、setattr()?函數用法_pyth
- 2022-10-17 C++智能指針模板應用詳細介紹_C 語言
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支