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

目录
  • 一、实现原理
  • 二、实现步骤
    • 1. nginx配置
    • 2. java springboot 后台权限验证
      • 2.1 权限校验文件下载
      • 2.2 权限校验文件预览
  • 三、扩展功能
    • 1. 下载统计、访问日志
      • 2. 下载限速
        • 3. 防盗链
          • 4. x-sendfile

          @date: 2020-07-31 06:00

          基于 nginx + java(springboot) 实现带权限验证的静态文件服务器,支持文件下载、pdf预览和图片预览

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

          一、实现原理

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

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

          nginx也具有此功能,但实现方式略有不同。在nginx中,此功能称为x-accel-redirect

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

          二、实现步骤

          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文件预览下载内容请搜索www.887551.com以前的文章或继续浏览下面的相关文章希望大家以后多多支持www.887551.com!

          (0)
          上一篇 2022年3月21日
          下一篇 2022年3月21日

          相关推荐