记一次浏览器预览通过nginx且有权限控制的静态文件

news2024/11/26 0:52:58

我的需求是,后台生成了合同文件,用户需要进行预览,如果采用流的实现方式的话,会涉及到输入流、输出流,性能开销较大,所以采用的是直接访问文件,这里就涉及到一个问题,就是

  1. 需要设置权限访问
  2. 不能把文件地址暴漏在浏览器

基于上述两个要求,我想到了forward转发,在权限校验通过之后,转发到文件服务地址。起初我的代码是这样的:

//前端代码
window.open("/house/contract/infoHouseCntr?cntrId=" + obj.data.idStr, "_blank");
@RequestMapping(value = "/infoHouseCntr", method = RequestMethod.GET)
    public void infoHouseCntr(Long cntrId, HttpServletRequest request, HttpServletResponse response) throws Exception {
        String path = iHouseCntrServiceImpl.infoHouseCntr(cntrId);
        request.getRequestDispatcher("/cntr-doc/"+path).forward(request,response);
//        response.sendRedirect("/cntr-doc/"+path); //有效
     
    }
// nginx配置
location ^~ /cntr-doc/  {	
   root   D:/**/**/templates/cntr-doc/;
}

在转发的时候,访问nginx,返回真正的文件路径到浏览器。

但是在写完之后,一直404,查看nginx的日志,错误日志、成功日志,都没有打印日志,所以,怀疑是转发后并没有访问nginx,也就不会被浏览。为了验证想法,就换成了重定向,因为重定向时,浏览器会重新发送请求,得到的结果是重定向可以访问到文件,不过地址也被暴漏到了浏览器。所以,通过这个,也得到了重定向与转发一个潜在的区别,但是在用到时,有可能就是致命的:转发不会经过nginx,而重定向会再次经过nginx。

接下来,开始实现正确的实现方式

参考博客:https://bbs.huaweicloud.com/blogs/360823

本质上是使用了X-Sendfile功能来实现,X-Sendfile 是一种将文件下载请求重定向到Web 服务器处理的机制,该Web服务器只需负责处理请求(例如权限验证),而无需执行读取文件并发送给用户的任务。

X-Sendfile可显著提高后台服务器的性能,消除了后端程序既要读文件又要处理发送的压力,尤其是处理大文件下载的情形下!

Nginx也具有此功能,但实现方式略有不同。在Nginx中,此功能称为X-Accel-Redirect。重点是X-Accel-Redirect配置返回服务器文件的真实路径,该路径返回后由Nginx内部请求处理,不会暴露给请求用户。

在这里插入图片描述

nginx配置

  1. 静态文件通过file_server访问,会被设置为internal,即只能内部访问不允许外部直接访问。
  2. 所有静态资源请求均被重定向到Java后台,经过权限验证后才能访问。
# 文件服务
 location ^~ /file {
	 # 内部请求(即一次请求的Nginx内部请求),禁止外部访问,重要。
	  internal;
	 # 文件路径
	 alias D:/data/ideaworkspace/apm/OnlineHouseAchieve/src/main/resources/templates/cntr-doc/;
	  limit_rate 200k;
	 # 浏览器访问返回200,然后转由后台处理
	  error_page 404 =200 @backend;
 }
 # 文件下载鉴权
 location @backend {
	 # 去掉访问路径中的 /file/,然后定义新的请求地址。/uecom/attach/$1 被替换的路径,$1表示参数不变
	  rewrite ^/file/(.*)$ /aa/bb/$1 break; # 这里注意一下替换的路径
	 # 这里的url后面不可以再拼接地址
	  proxy_pass http://127.0.0.1:8080;
	  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;
 }

前端访问

window.open("/file/preview_file?cntrId=" + obj.data.idStr, "_blank");

后台代码

@GetMapping("/aa/bb/preview_file")
public void previewFile(Long cntrId,HttpServletRequest request, HttpServletResponse response) throws IOException {
    String path = iHouseCntrServiceImpl.infoHouseCntr(cntrId);

    // 已被授权访问
    // 文件直接显示
    response.setHeader("Content-Disposition", "inline; filename=\"" + new String(path.getBytes("GBK"), "iso-8859-1") + "\"");
    if (path.endsWith("pdf")) {
        // PDF
        response.setHeader("Content-Type", "application/pdf;charset=utf-8");
    } else {
        // 图片
        response.setHeader("Content-Type", "image/*;charset=utf-8");
    }
    // 返回真实文件路径交由 Nginx 处理,保证前端无法看到真实的文件路径。
    // 这里的 "/file" 为 Nginx 中配置的下载服务名
    response.setHeader("X-Accel-Redirect", "/file/" + path);
    // 浏览器缓存 1 小时
    response.setDateHeader("Expires", System.currentTimeMillis() + 1000 * 60 * 60);
}

同样的,下载也是这个意思,只不过需要在response返回时告诉浏览器执行下载还是执行打开预览。即response.setHeader(“Content-Disposition”, "inline; filename=“xxx.pdf”);, 然后修改返回数据的格式Content-Type即可。告诉浏览器,你是想预览还是下载。

/**
       * @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

比如:
// 限速,单位字节,默认不限
response.setHeader(“X-Accel-Limit-Rate”,“1024”);

防盗链

//判断 Referer
String referer = request.getHeader("Referer");
if (referer == null || !referer.startsWith("https://www.itmm.wang")) {
   // 无权限访问,跳转403
   this.errorPage(403);
   return;
}

X-Sendfile

X-Sendfile是一项功能,每个代理服务器都有自己不同的实现。

Web ServerHeader
NginxX-Accel-Redirect
ApacheX-Sendfile
LighttpdX-LIGHTTPD-send-file
SquidX-Accelerator-Vary

使用 X-SendFile 的缺点是你失去了对文件传输机制的控制。例如如果你希望在完成文件下载后执行某些操作,比如只允许用户下载文件一次,这个 X-Sendfile 是没法做到的,因为请求交给了后台,你并不知道下载是否成功。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/89098.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Mobtech秒验SDK——一站式解决用户登录场景

据悉,北京中文万维科技有限公司旗下多款APP,和MobTech开发的秒验SDK达成合作,为其提供用户一键登录解决方案。 北京中文万维科技有限公司是一家立志以移动互联网阅读为发展起点的阅读互动娱乐高新技术企业,旗下拥有多款阅读类APP。…

互动教学场景下的视频直播线上研讨会应用(组图)

阿酷TONY / 原创 / 2022-12-14 / 长沙 / 互动教学/互动培训类场景特点: 1. 直播过程中,学员不仅是观看讲师的授课内容,还要与讲师直接进行音视频会话;当然一些非培训教学场景也常用到,比如线上学术类研讨会等等。 2…

【Pycharm教程】 详解 PyCharm Macros宏

宏提供了一种方便的方法来自动化您在编写代码时经常执行的重复过程。您可以录制、编辑和播放宏,为它们分配快捷方式并共享它们。 宏可用于在文件中组合一系列与编辑器相关的操作。 您无法记录按钮单击、导航到弹出窗口以及访问工具窗口、菜单和对话框。 可以使用没…

从3s到40ms,看看人家的性能优化技巧,确实优雅

什么是高性能系统 先理解一下什么是高性能设计,官方定义: 高可用(High Availability,HA)核心目标是保障业务的连续性,从用户视角来看,业务永远是正常稳定的对外提供服务,业界一般用几个 9 来衡量系统的可用性。通常采…

代码随想录算法训练营第五十天|123.买卖股票的最佳时机III、 188.买卖股票的最佳时机IV

123.买卖股票的最佳时机III 此题限定了买卖的次数,所以应该用几个状态来记录所对应得利润 至多买卖两次,这意味着可以买卖一次,可以买卖两次,也可以不买卖。 dp数组及下标含义 一天一共就有五个状态, 0 没有操作 1 第…

java+mysql 基于ssm的驾校预约管理系统

随着现代驾校预约管理的快速发展,可以说驾校预约管理已经逐渐成为现代驾校预约管理过程中最为重要的部分之一。但是一直以来我国传统的驾校预约管理并没有建立一套完善的行之有效的驾校预约管理系统,传统的驾校预约管理已经无法适应高速发展,无论是从效率还是从效果来看都远远的…

代码随想录Day50|123.买卖股票的最佳时机III、188.买卖股票的最佳时机IV

文章目录123.买卖股票的最佳时机III188.买卖股票的最佳时机IV123.买卖股票的最佳时机III 文章讲解:代码随想录 (programmercarl.com) 题目链接:123. 买卖股票的最佳时机 III - 力扣(LeetCode) 题目: 给定一个数组&…

[附源码]Python计算机毕业设计感动校园人物投稿网站Django(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程 项目运行 环境配置: Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术: django python Vue 等等组成,B/S模式 pychram管理等…

极简入门 2022 docker 部署skywalking9.2.0

安装skywalking服务 官方地址:官网 安装文档:文档地址 拉取镜像 docker pull apache/skywalking-oap-server:9.2.0 docker pull apache/skywalking-ui:9.2.0运行skywalking-oap容器 docker run --name skywalking-oap -e TZAsia/Shanghai -p 12800:1…

ffmpeg的基本用法

title: ffmpeg的基本用法 categories:[ffmpeg] tags:[音视频编程] 一、ffmpeg的安装 1.Centos安装 FFmpeg 在默认的CentOS 8 源仓库中没有提供。你可以选择通过源文件编译安装 FFmpeg,或者使用dnf工具从Negativo17源仓库中安装。我们将会使用第二个选项。 完成…

正大期货数据整合 新鲜事简单报

本周观察重点: 🔹美国11月CPI数据昨晚公布为7.1%,低于市场预期7.3%,较于前值7.7%大幅回落,随通膨回落,静待联准会未来货币政策方向。 12/15 (四) 美国、香港、欧洲、英国、台湾央行12月利率决策会议 12/16 (六) 日本、…

基于java+springboot+mybatis+vue+mysql的小学家校一体作业帮

项目介绍 本系统采用java语言开发,后端采用springboot框架,前端采用vue技术,数据库采用mysql进行数据存储。系统功能如下: 前台: 首页、微社区、试卷、公告通知、个人中心、后台管理 后台: 首页、个人中…

SpringCloud02

1.Nacos配置管理 Nacos除了可以做注册中心,同样可以做配置管理来使用。 1.1.统一配置管理 当微服务部署的实例越来越多,达到数十、数百时,逐个修改微服务配置就会让人抓狂,而且很容易出错。我们需要一种统一配置管理方案&#xff…

技术分享 | 软件项目管理与跨部门沟通协作

项目管理是在项目活动中运用知识、技能、工具和方法,以便达到项目要求。 软件项目管理有其特定的对象、范围和活动,着重关注成本、进度、风险和质量的管理,还需要协调开发团队和客户的关系,协调内部各个团队之间的关系&#xff0…

怎么把pdf格式转成word文档?如何将 PDF 转换为 Word

怎么把pdf格式转成word文档?PDF是运用得很广泛地的办公文档,但是不能编辑,为了方便编辑,需要将PDF转换为word,那么,如何将 PDF 转换为 Word,下面,易我小编会讲解实用的pdf转word的操…

基于VBA实现电缆结构自动出图(二) —— 单芯线

大家敢相信吗,原来VBA竟然可以实现电缆结构自动出图,换句话说,只要输入数据,VBA会自动将电缆的结构画出来,同时还可以渲染,结果竟然不输画图软件,真真让我刮目相看。这里我就不过多介绍VBA了&am…

传奇登录器列表显示不连接

传奇登录器列表显示不连接 我们打开登录器后列表显示不连接怎么办? 图中有打码部分望读者理解(平台gz) 以下我们大家说下几种列表显示不连接的几种情况 第一种:列表格式错误 在我们开区的位置默认是127.0.0.1 端口默认7000 如果…

Django captcha验证码应用【亲测可用】

1、下载captcha安装包: pip install django-simple-captcha pip3 install pillow -i Simple Index 2、在settings的INSTALLED_APPS添加: captcha, 3、在控制台执行: 生成迁移文件:python3 manage.py makemigrations 迁移文件…

数据流标准差计算方法-不用事先计算均值

数据流标准差计算方法-不用事先计算均值 挑战 标准差(Standard Deviation)是误差有效值的估计,在信号处理里面代表偏差有效值。公式定义需要事先知道均值(引自百度百科): 数据流进来,你事先不知道均值,…

【云原生】k8s中kubectl陈述式资源管理

内容预知 1.k8s陈述资源管理方法的说明 1.1 管理k8s核心资源的三种基本方法 陈述式资源管理方法 声明式资源管理方法 GUI式资源管理方法 1.2 kubectl命令行工具 的说明 2.k8s集群中的基本信息查看 2.1 k8s中基本管理信息的查看 (1)查看版本信息 …