NGINX 权限控制文件预览和下载的实现原理


Posted in Servers onJanuary 18, 2022

@date: 2020-07-31 06:00

基于 Nginx + Java(SpringBoot) 实现带权限验证的静态文件服务器,支持文件下载、PDF预览和图片预览

需要注意的是,无需权限判断的图片不建议使用此方法,大量的图片访问会增加后台服务器的处理压力。

一、实现原理

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

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

Nginx也具有此功能,但实现方式略有不同。在Nginx中,此功能称为X-Accel-Redirect

用户请求文件,权限控制时序图:

NGINX 权限控制文件预览和下载的实现原理

二、实现步骤

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

到此这篇关于NGINX 权限控制文件预览和下载的文章就介绍到这了,更多相关nginx文件预览下载内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Servers 相关文章推荐
解决Nginx 配置 proxy_pass 后 返回404问题
Mar 31 Servers
nginx配置proxy_pass中url末尾带/与不带/的区别详解
Mar 31 Servers
扩展多台相同的Web服务器
Apr 01 Servers
Nginx已编译的nginx-添加新模块
Apr 01 Servers
nginx+lua单机上万并发的实现
May 31 Servers
使用Nginx的访问日志统计PV与UV
May 06 Servers
Windows Server 2008配置防火墙策略详解
Jun 28 Servers
Linux中sftp常用命令整理
Jun 28 Servers
nginx静态资源的服务器配置方法
Jul 07 Servers
django项目、vue项目部署云服务器的详细过程
Jul 23 Servers
阿里云服务器(windows)手动部署FTP站点详细教程
Aug 05 Servers
Windows server 2016服务器基本设置
Aug 14 Servers
Nginx虚拟主机的搭建的实现步骤
Jan 18 #Servers
Nginx下SSL证书安装部署步骤介绍
Dec 06 #Servers
教你快速构建一个基于nginx的web集群项目
Nov 27 #Servers
Linux安装apache服务器的配置过程
Nov 27 #Servers
Nginx配置https的实现
nginx内存池源码解析
苹果M1芯片安装nginx 并且部署vue项目步骤详解
You might like
详解PHP的Yii框架中扩展的安装与使用
2016/04/01 PHP
数组任意位置插入元素,删除特定元素的实例
2017/03/02 PHP
PHP封装cURL工具类与应用示例
2019/07/01 PHP
XENON基于JSON变种
2010/07/27 Javascript
基于jquery的动态创建表格的插件
2011/04/05 Javascript
浅谈JSON.stringify()和JOSN.parse()方法的不同
2016/08/29 Javascript
对Angular.js Controller如何进行单元测试
2016/10/25 Javascript
javascript-解决mongoose数据查询的异步操作
2016/12/22 Javascript
从零学习node.js之express入门(六)
2017/02/25 Javascript
JS仿淘宝搜索框用户输入事件的实现
2017/06/19 Javascript
Vue 动态组件components和v-once指令的实现
2019/08/30 Javascript
ES6实现图片切换特效代码
2020/01/14 Javascript
[00:33]DOTA2上海特级锦标赛 CDEC战队宣传片
2016/03/04 DOTA
[01:26]神话结束了,却也刚刚开始——DOTA2新英雄玛尔斯驾临战场
2019/03/10 DOTA
简单讲解Python编程中namedtuple类的用法
2016/06/21 Python
Python实现带参数与不带参数的多重继承示例
2018/01/30 Python
[原创]Python入门教程5. 字典基本操作【定义、运算、常用函数】
2018/11/01 Python
Python数据处理篇之Sympy系列(五)---解方程
2019/10/12 Python
Python倒排索引之查找包含某主题或单词的文件
2019/11/13 Python
Python读取csv文件实例解析
2019/12/30 Python
opencv+python实现均值滤波
2020/02/19 Python
Python面向对象程序设计之类和对象、实例变量、类变量用法分析
2020/03/23 Python
python开发前景如何
2020/06/11 Python
Python 3.9的到来到底是意味着什么
2020/10/14 Python
Python实现Appium端口检测与释放的实现
2020/12/31 Python
Html5 Geolocation获取地理位置信息实例
2016/12/09 HTML / CSS
Skyscanner阿联酋:全球领先的旅游搜索平台
2017/11/25 全球购物
简单而又朴实的个人求职信分享
2013/12/12 职场文书
大学军训感言400字
2014/03/11 职场文书
路政管理求职信
2014/06/18 职场文书
幼儿园五一劳动节活动总结
2015/02/09 职场文书
锦旗赠语
2015/06/23 职场文书
植树节新闻稿
2015/07/17 职场文书
感恩父母主题班会
2015/08/12 职场文书
安全主题班会教案
2015/08/12 职场文书
Go 语言结构实例分析
2021/07/04 Golang