Nginx訪問FTP服務(wù)器文件的時效性/安全校驗的方法
背景
FTP文件服務(wù)器在我們?nèi)粘i_發(fā)中經(jīng)常使用,在項目中我們經(jīng)常把FTP文件下載到內(nèi)存中,然后轉(zhuǎn)為base64給前端進行展示。如果excel中也需要導(dǎo)出圖片,數(shù)據(jù)量大的情況下會直接返回一個后端的開放接口地址,然后在項目中對接口的參數(shù)進行鑒權(quán),或者實效性檢驗等,最后從FTP下載圖片用流的方式傳到瀏覽器中。
但是這種方式會加大內(nèi)存的消耗,所有的文件相關(guān)的都在內(nèi)存中下載回傳給前端;報表下載的數(shù)據(jù)量很大的情況下服務(wù)很容易拖垮。所以就設(shè)想通過兩層nginx反向代理的方式是否可以滿足文件的直接訪問。
假設(shè)FTP文件服務(wù)器的照片存放地址為:/upload/signature
傳統(tǒng)實現(xiàn)
首先我們在下載excel的時候需要組裝一個url,如下所示的get請求就是一個對外開放無需權(quán)限的接口,真實情況下會對realFilePath進行加密組裝,里面放一些timestamp或者redis的key來驗證實效性、安全性等。
@GetMapping("/signatureImage/{path}") public void signatureImage(@PathVariable("path") String realFilePath, HttpServletResponse response) throws IOException { //realFilePath = "/20231206/qhyu.png" String fileName = "qhyu.png"; //可以切割獲取。 String path = "/upload/signature"; // 固定的存放路徑 ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); try (Ftp ftp = new Ftp("Ftp_address", "Ftp_port", "username", "password")) { ftp.download(path+realFilePath, fileName, outputStream); } catch (Exception e) { log.error("FTP文件下載出錯:{}", e.getMessage()); throw new QhyuException(MessageCode.FILE_DOENLOAD_ERROR.getCode()); } // 將內(nèi)存中的文件內(nèi)容轉(zhuǎn)換為輸入流 InputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray()); // 設(shè)置響應(yīng)的內(nèi)容類型為圖片格式 String contentType = MediaType.IMAGE_PNG_VALUE; // 假設(shè)為PNG格式 response.setContentType(contentType); org.apache.commons.io.IOUtils.copy(inputStream, response.getOutputStream()); response.flushBuffer(); // 關(guān)閉內(nèi)存流和FTP連接 inputStream.close(); outputStream.close(); }
Nginx實現(xiàn)
要通過Nginx實現(xiàn)的話,基本上網(wǎng)上的方案都是讓使用lua。雖然可以但是沒必要。因為我從官網(wǎng)上找到了解決方案,如下所示。
提供一下proxy相關(guān)參數(shù)的含義:
proxy_set_header Host $host;
:
此參數(shù)設(shè)置了將客戶端請求中的Host頭部信息傳遞給代理服務(wù)器。$host變量表示客戶端請求中的主機名。
proxy_intercept_errors on;
:
當(dāng)啟用此參數(shù)時,代理服務(wù)器會攔截后端服務(wù)器返回的錯誤響應(yīng),并將其作為代理服務(wù)器的響應(yīng)返回給客戶端。這允許代理服務(wù)器處理后端服務(wù)器的錯誤響應(yīng),并可以進行自定義的錯誤處理。
proxy_set_header X-Real-IP $remote_addr;
:
此參數(shù)設(shè)置了將客戶端的真實IP地址傳遞給代理服務(wù)器。$remote_addr變量表示客戶端的IP地址。
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
:
此參數(shù)設(shè)置了將客戶端的IP地址添加到X-Forwarded-For頭部信息中。$proxy_add_x_forwarded_for變量表示將客戶端IP地址添加到現(xiàn)有的X-Forwarded-For頭部信息中。
proxy_buffering off;
:
當(dāng)啟用此參數(shù)時,禁用代理緩沖。代理緩沖可以在接收完整的響應(yīng)后再將其發(fā)送給客戶端,以提高性能和效率。禁用緩沖意味著代理服務(wù)器會立即將收到的數(shù)據(jù)發(fā)送給客戶端,適用于需要實時數(shù)據(jù)傳輸?shù)膱鼍啊?/p>
下面就是我的nginx配置:
server { listen 10086; charset utf-8; access_log /var/log/nginx/qhyu/qhyu_access.log; error_log /var/log/nginx/qhyu/qhyu_error.log; location /verify { proxy_pass http://host:port/api/signatureImage/validate?realFilePath=$arg_realFilePath&signature=$arg_signature; proxy_set_header Host $host; proxy_intercept_errors on; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_buffering off; error_page 418 = @custom_redirect; error_page 420 = @custom_error; } location @custom_error{ default_type application/json; return 200 'error image'; } location @custom_redirect { rewrite ^ /signature/$arg_realFilePath last; } location /signature { alias /upload/signature/; } } }
然后就是編寫了一個接口,也就是在調(diào)用的時候可以訪問/verify接口帶上參數(shù),就會跳轉(zhuǎn)到這個接口進行校驗,如果返回的http狀態(tài)碼是418說明校驗通過,如果返回的是420說明校驗失敗。
@GetMapping("/signatureImage/validate") public Object signatureValidate(String realFilePath,String signature,HttpServletResponse httpServletResponse){ String redisKey = AesEncryptUtil.decryption(signature); if (StrUtil.isBlank(redisKey)){ httpServletResponse.setStatus(420); return RenderResult.success(); } Object value = redisService.get(redisKey); if (value == null) { httpServletResponse.setStatus(420); return RenderResult.success(); } List<String> signatureUrls = JSON.parseArray(JSON.toJSONString(value), String.class); if (signatureUrls == null || signatureUrls.isEmpty()){ httpServletResponse.setStatus(420); return RenderResult.success(); } if (!signatureUrls.contains(realFilePath)){ httpServletResponse.setStatus(420); return RenderResult.success(); } // greater than or equal to 300 should be passed to a client or be intercepted and redirected to nginx // for processing with the error_page directive // http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_intercept_errors httpServletResponse.setStatus(418); return RenderResult.success(); }
分析結(jié)果
nginx的實現(xiàn)方式在校驗失敗的時候頁面返回error image,跳轉(zhuǎn)的是420 error_page;成功的時候會訪問FTP文件服務(wù)器的路徑,反正圖片到頁面展示。在實際的開發(fā)過程中,外層可能還會有一個nginx反向代理,本文主要講解了一下如何使用這個方式對訪問的文件進行鑒權(quán)。
這樣做的好處就是不需要每個文件都下載到內(nèi)存然后使用流的方式傳輸,直接訪問的方式減少了后端服務(wù)的壓力,并且像頭像、簽名這種可能訪問頻繁的接口使用這種方式來處理是很棒的一種方式。
主要的思路就是拿到proxy_pass的返回信息,如果使用lua的話可以獲取到我們返回的body內(nèi)容,但是不使用lua的時候我們可以迂回處理,使用status code也一樣可以達到目的。
到此這篇關(guān)于Nginx訪問FTP服務(wù)器文件的時效性/安全校驗的文章就介紹到這了,更多相關(guān)Nginx訪問FTP服務(wù)器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
記一次nginx配置不當(dāng)引發(fā)的499與failover 機制失效問題
近期在非高峰期也存在499超過告警閾值的偶發(fā)情況,多的時候一天幾次,少的時候則幾天一次,持續(xù)一般也就數(shù)分鐘,經(jīng)過和小伙伴的共同探究,最后發(fā)現(xiàn)之前對于499是客戶端主動斷開因而和服務(wù)端關(guān)系不大的想當(dāng)然認(rèn)知是錯誤的,這里記錄一下2023-05-05nginx反向代理https內(nèi)部定向到http報302的問題及解決
這篇文章主要介紹了nginx反向代理https內(nèi)部定向到http報302的問題及解決,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-12-12詳解Nginx 反向代理、負載均衡、頁面緩存、URL重寫及讀寫分離詳解
本篇文章主要介紹了Nginx 反向代理、負載均衡、頁面緩存、URL重寫及讀寫分離詳解,具有一定的參考價值,感興趣的小伙伴們可以參考一下。2016-12-12阿里云國際版使用Nginx作為HTTPS轉(zhuǎn)發(fā)代理服務(wù)器的處理方法
本文介紹了使用NGINX作為HTTPS流量轉(zhuǎn)發(fā)代理的兩種方法。它總結(jié)了NGINX使用HTTP?CONNECT隧道和NGINX流充當(dāng)HTTPS轉(zhuǎn)發(fā)代理的解決方案的原則,環(huán)境構(gòu)建要求,應(yīng)用場景和關(guān)鍵問題2022-05-05