日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

NGINX?權限控制文件預覽和下載的實現原理_nginx

作者:隔壁老瓦 ? 更新時間: 2022-03-27 編程語言

@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 是沒法做到的,因為請求交給了后臺,你并不知道下載是否成功。

參考資料

  1. X-Sendfile-從Web應用程序有效地提供大型靜態文件
  2. Nginx-X-Accel-Redirect
  3. Nginx -XSendfile
  4. Content-Disposition
  5. Http Content-Type

原文鏈接:https://blog.csdn.net/wxb880114/article/details/122430464

欄目分類
最近更新