Nginx访问FTP服务器文件的时效性/安全校验
背景
FTP文件服务器在我们日常开发中经常使用,在项目中我们经常把FTP文件下载到内存中,然后转为base64给前端进行展示。如果excel中也需要导出图片,数据量大的情况下会直接返回一个后端的开放接口地址,然后在项目中对接口的参数进行鉴权,或者实效性检验等,最后从FTP下载图片用流的方式传到浏览器中。
但是这种方式会加大内存的消耗,所有的文件相关的都在内存中下载回传给前端;报表下载的数据量很大的情况下服务很容易拖垮。所以就设想通过两层nginx反向代理的方式是否可以满足文件的直接访问。
假设FTP文件服务器的照片存放地址为:/upload/signature
传统实现
首先我们在下载excel的时候需要组装一个url,如下所示的get请求就是一个对外开放无需权限的接口,真实情况下会对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());}// 将内存中的文件内容转换为输入流InputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());// 设置响应的内容类型为图片格式String contentType = MediaType.IMAGE_PNG_VALUE; // 假设为PNG格式response.setContentType(contentType);org.apache.commons.io.IOUtils.copy(inputStream, response.getOutputStream());response.flushBuffer();// 关闭内存流和FTP连接inputStream.close();outputStream.close();}
Nginx实现
要通过Nginx实现的话,基本上网上的方案都是让使用lua。虽然可以但是没必要。因为我从官网上找到了解决方案,如下所示。

提供一下proxy相关参数的含义:
proxy_set_header Host $host;:
此参数设置了将客户端请求中的Host头部信息传递给代理服务器。$host变量表示客户端请求中的主机名。proxy_intercept_errors on;:
当启用此参数时,代理服务器会拦截后端服务器返回的错误响应,并将其作为代理服务器的响应返回给客户端。这允许代理服务器处理后端服务器的错误响应,并可以进行自定义的错误处理。proxy_set_header X-Real-IP $remote_addr;:
此参数设置了将客户端的真实IP地址传递给代理服务器。$remote_addr变量表示客户端的IP地址。proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;:
此参数设置了将客户端的IP地址添加到X-Forwarded-For头部信息中。$proxy_add_x_forwarded_for变量表示将客户端IP地址添加到现有的X-Forwarded-For头部信息中。proxy_buffering off;:
当启用此参数时,禁用代理缓冲。代理缓冲可以在接收完整的响应后再将其发送给客户端,以提高性能和效率。禁用缓冲意味着代理服务器会立即将收到的数据发送给客户端,适用于需要实时数据传输的场景。
下面就是我的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/;}
}
}
然后就是编写了一个接口,也就是在调用的时候可以访问/verify接口带上参数,就会跳转到这个接口进行校验,如果返回的http状态码是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_errorshttpServletResponse.setStatus(418);return RenderResult.success();}
分析结果
nginx的实现方式在校验失败的时候页面返回error image,跳转的是420 error_page;成功的时候会访问FTP文件服务器的路径,反正图片到页面展示。在实际的开发过程中,外层可能还会有一个nginx反向代理,本文主要讲解了一下如何使用这个方式对访问的文件进行鉴权。
这样做的好处就是不需要每个文件都下载到内存然后使用流的方式传输,直接访问的方式减少了后端服务的压力,并且像头像、签名这种可能访问频繁的接口使用这种方式来处理是很棒的一种方式。
主要的思路就是拿到proxy_pass的返回信息,如果使用lua的话可以获取到我们返回的body内容,但是不使用lua的时候我们可以迂回处理,使用status code也一样可以达到目的。
相关文章:
Nginx访问FTP服务器文件的时效性/安全校验
背景 FTP文件服务器在我们日常开发中经常使用,在项目中我们经常把FTP文件下载到内存中,然后转为base64给前端进行展示。如果excel中也需要导出图片,数据量大的情况下会直接返回一个后端的开放接口地址,然后在项目中对接口的参数进…...
【VSCode】自定义配置
VSCode自定义配置 Visual Studio Code (VSCode) 是一个强大的开源代码编辑器,支持丰富的自定义配置。下面是一些常见的自定义配置选项,你可以根据个人喜好和工作流程进行调整: 1. 主题和配色方案: 在 “settings.json” 中设置:…...
SpringBoot整合Kafka (一)
📑前言 本文主要讲了SpringBoot整合Kafka文章⛺️ 🎬作者简介:大家好,我是青衿🥇 ☁️博客首页:CSDN主页放风讲故事 🌄每日一句:努力一点,优秀一点 目录 文章目录 &…...
随机分词与tokenizer(BPE->BBPE->Wordpiece->Unigram->sentencepiece->bytepiece)
0 tokenizer综述 根据不同的切分粒度可以把tokenizer分为: 基于词的切分,基于字的切分和基于subword的切分。 基于subword的切分是目前的主流切分方式。subword的切分包括: BPE(/BBPE), WordPiece 和 Unigram三种分词模型。其中WordPiece可以认为是一种特殊的BPE。完…...
成都工业学院Web技术基础(WEB)实验四:CSS3布局应用
写在前面 1、基于2022级计算机大类实验指导书 2、代码仅提供参考,前端变化比较大,按照要求,只能做到像,不能做到一模一样 3、图片和文字仅为示例,需要自行替换 4、如果代码不满足你的要求,请寻求其他的…...
TikTok科技趋势:平台如何引领数字社交革命?
TikTok作为一款颠覆性的短视频应用,不仅改变了用户的娱乐方式,更在数字社交领域引领了一场革命。本文将深入探讨TikTok在科技趋势方面的引领作用,分析其在数字社交革命中的关键角色,以及通过技术创新如何不断满足用户需求…...
【上海大学数字逻辑实验报告】六、时序电路
一、 实验目的 掌握同步二进制计数器和移位寄存器的原理。学会用分立元件构成2位同步二进制加计数器。学会在Quartus II上设计单向移位寄存器。学会在Quartus II上设计环形计数器。 二、 实验原理 同步计数器是指计数器中的各触发器的时钟脉冲输入端连接在一起,接…...
docker版zerotier-planet服务端搭建
1:ZeroTier 介绍2:为什么要自建PLANET 服务器3:开始安装 3.1:准备条件 3.1.1 安装git3.1.2 安装docker3.1.3 启动docker3.2:下载项目源码3.3:执行安装脚本3.4 下载 planet 文件3.5 新建网络 3.5.1 创建网络4.客户端配置 4.1 Windows 配置 4.2 加入网络4.2 Linux 客户端4.…...
【Spring教程28】Spring框架实战:从零开始学习SpringMVC 之 请求与请求参数详解
目录 1 设置请求映射路径1.1 环境准备 1.2 问题分析1.3 设置映射路径 2 请求参数2.1 环境准备2.2 参数传递2.2.1 GET发送单个参数2.2.2 GET发送多个参数2.2.3 GET请求中文乱码2.2.4 POST发送参数2.2.5 POST请求中文乱码 欢迎大家回到《Java教程之Spring30天快速入门》ÿ…...
node.js和浏览器之间的区别
node.js是什么 Node.js是一种基于Chrome V8引擎的JavaScript运行环境,可以在服务器端运行JavaScript代码 Node.js 在浏览器之外运行 V8 JavaScript 引擎。 这使得 Node.js 非常高效。 浏览器如何运行js代码 nodejs运行环境 在浏览器中,大部分时间你所…...
【python并发任务的几种方式】
文章目录 1 Process:2 Thread:3 ThreadPoolExecutor:4 各种方式的优缺点:5 线程与进程的结束方式5.1 线程结束的几种方式5.2 进程的结束方式 6 应用场景效率对比 在Python中,有几种方法可以处理并行执行任务。其中,Process、Thread和ThreadPo…...
使用ROS模板基于ECS和RDS创建WordPress环境
本文教程介绍如何使用ROS模板基于ECS和RDS(Relational Database Service)创建WordPress环境。 前提条件 如果您是首次使用ROS,必须先开通ROS服务。ROS服务免费,开通服务不会产生任何费用。 背景信息 WordPress是使用PHP语言开…...
龙迅LT2611UXC 双PORT LVDS转HDMI(2.0)+音频
描述: LT2611UXC是一个高性能的LVDS到HDMI2.0的转换器,用于STB,DVD应用程序。 LVDS输入可配置为单端口或双端口,有1个高速时钟通道,3~4个高速数据通道,最大运行1.2Gbps/通道,可支持高达9.6Gbp…...
websocket和SSE通信示例(无需安装任何插件)
websocket和SSE通信示例(无需安装任何插件) 源码示例(两种方案任意切换) data(){return {heartBeatInterval:5000,// 心跳间隔时间,单位为毫秒webSocket:null,heartBeatTimer:null,} }, mounted() {// this.initWebS…...
计算机网络(三)
(十一)路由算法 A、路由算法分类 动态路由和静态路由 静态路由:人工配制,路由信息更新慢,优先级高。这种在实际网络中要投入成本大,准确但是可行性弱。 动态路由:路由更新快,自动…...
HttpURLConnection OOM问题记录
使用HttpURLConnection 上传大文件,会出现内存溢出问题: 观察HttpURLConnection 源码: Overridepublic synchronized OutputStream getOutputStream() throws IOException {connecting true;SocketPermission p URLtoSocketPermission(th…...
WT588F02B单片机语音芯片在磁疗仪中的应用介绍
随着健康意识的普及和科技的发展,磁疗仪作为一种常见的理疗设备,受到了广大用户的关注。为了提升用户体验和操作便捷性,唯创知音WT588F02B单片机语音芯片被成功应用于磁疗仪中。这一结合将为磁疗仪带来智能化的语音交互功能,为用户…...
深度学习——第5章 神经网络基础知识
第5章 神经网络基础知识 目录 5.1 由逻辑回归出发 5.2 损失函数 5.3 梯度下降 5.4 计算图 5.5总结 在第1课《深度学习概述》中,我们介绍了神经网络的基本结构,了解了神经网络的基本单元组成是神经元。如何构建神经网络,如何训练、优化神…...
微信网页授权步骤说明
总览 引导用户进入授权页面同意授权,获取code通过code换取网页授权access_token(与基础支持中的access_token不同)如果需要,开发者可以刷新网页授权access_token,避免过期(一般不需要)通过网页…...
linux bash shell变量操作符 —— 筑梦之路
1. 变量子串 ${var} 返回变量var的内容,单独使用时有没有{}一样,混合多个变量和常量时,用{}界定变量名 ${#var} 返回变量var内容的长度 ${var:offset} 从变量var中的偏移量offset开始截取到字符串结尾的子字符串,offset从0开始 ${…...
在软件开发中正确使用MySQL日期时间类型的深度解析
在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...
【WiFi帧结构】
文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成:MAC头部frame bodyFCS,其中MAC是固定格式的,frame body是可变长度。 MAC头部有frame control,duration,address1,address2,addre…...
Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)
目录 1.TCP的连接管理机制(1)三次握手①握手过程②对握手过程的理解 (2)四次挥手(3)握手和挥手的触发(4)状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...
cf2117E
原题链接:https://codeforces.com/contest/2117/problem/E 题目背景: 给定两个数组a,b,可以执行多次以下操作:选择 i (1 < i < n - 1),并设置 或,也可以在执行上述操作前执行一次删除任意 和 。求…...
HTML前端开发:JavaScript 常用事件详解
作为前端开发的核心,JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例: 1. onclick - 点击事件 当元素被单击时触发(左键点击) button.onclick function() {alert("按钮被点击了!&…...
JDK 17 新特性
#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持,不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的ÿ…...
uniapp中使用aixos 报错
问题: 在uniapp中使用aixos,运行后报如下错误: AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...
实现弹窗随键盘上移居中
实现弹窗随键盘上移的核心思路 在Android中,可以通过监听键盘的显示和隐藏事件,动态调整弹窗的位置。关键点在于获取键盘高度,并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...
OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 在 GPU 上对图像执行 均值漂移滤波(Mean Shift Filtering),用于图像分割或平滑处理。 该函数将输入图像中的…...
ip子接口配置及删除
配置永久生效的子接口,2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...
