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的反向代理实例详解
Mar 31 Servers
Nginx配置80端口访问8080及项目名地址方法解析
Mar 31 Servers
nginx配置虚拟主机的详细步骤
Jul 21 Servers
使用 Apache 反向代理的设置技巧
Jan 18 Servers
Nginx图片服务器配置之后图片访问404的问题解决
Mar 21 Servers
Nginx工作模式及代理配置的使用细节
Mar 21 Servers
docker-compose部署Yapi的方法
Apr 08 Servers
Windows server 2012搭建FTP服务器
Apr 29 Servers
tomcat默认最大连接数及相关调整方法
May 06 Servers
阿里云国际版 使用Nginx作为HTTPS转发代理服务器
May 11 Servers
Nginx安装配置详解
Jun 25 Servers
Nginx 502 bad gateway错误解决的九种方案及原因
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
剧场版动画《PSYCHO-PASS 3 FIRST INSPECTOR》3月27日日本上映!
2020/03/06 日漫
PHP-redis中文文档介绍
2013/02/07 PHP
php之readdir函数用法实例
2014/11/13 PHP
json 实例详细说明教程
2009/10/31 Javascript
关于JavaScript对象的动态选择及遍历对象
2014/03/10 Javascript
AngularJS入门知识之MVW类框架的编程思想探讨
2014/12/08 Javascript
jQuery往textarea中光标所在位置插入文本的方法
2015/06/26 Javascript
JavaScript实现获得所有兄弟节点的方法
2015/07/23 Javascript
通过bootstrap全面学习less
2016/11/09 Javascript
微信小程序picker组件下拉框选择input输入框的实例
2017/09/20 Javascript
学习Vue组件实例
2018/04/28 Javascript
layui实现文件或图片上传记录
2018/08/28 Javascript
js array数组对象操作方法汇总
2019/03/18 Javascript
如何解决js函数防抖、节流出现的问题
2019/06/17 Javascript
layui文件上传控件带更改后数据传值的方法
2019/09/23 Javascript
JS数据类型分类及常用判断方法
2020/11/19 Javascript
Python字符串处理函数简明总结
2015/04/13 Python
用python记录运行pid,并在需要时kill掉它们的实例
2017/01/16 Python
快速查询Python文档方法分享
2017/12/27 Python
Python中整数的缓存机制讲解
2019/02/16 Python
浅析python的Lambda表达式
2019/02/27 Python
解决webdriver.Chrome()报错:Message:'chromedriver' executable needs to be in Path
2019/06/12 Python
Python 生成一个从0到n个数字的列表4种方法小结
2019/11/28 Python
Pandas将列表(List)转换为数据框(Dataframe)
2020/04/24 Python
python 中的命名空间,你真的了解吗?
2020/08/19 Python
jupyter使用自动补全和切换默认浏览器的方法
2020/11/18 Python
详解canvas在圆弧周围绘制文本的两种写法
2018/05/22 HTML / CSS
Omio葡萄牙:全欧洲低价大巴、火车和航班搜索和比价
2019/02/09 全球购物
英文自荐信
2013/12/15 职场文书
小学岗位竞聘方案
2014/01/22 职场文书
报效祖国演讲稿
2014/09/15 职场文书
科级干部群众路线教育实践活动对照检查材料思想汇报
2014/09/20 职场文书
教师工作能力自我评价
2015/03/04 职场文书
校车安全管理责任书
2015/05/11 职场文书
Python闭包的定义和使用方法
2022/04/11 Python
vue组件vue-esign实现电子签名
2022/04/21 Vue.js