NGINX文件签名下载验证服务

在WEB架构中经常会遇到私密文件下载服务,比如网盘、OA、邮箱等等。但是大文件用动态语言来验证权限并输出是非常消耗性能的事情。今天用Nginx+PHP实现了简单的文件签名下载服务(感谢同事Toad的辛苦调试)。

原理就是业务给用户的下载地址是真实文件的下载地址加上授权签名(Signature),当用户的下载请求到服务器时,用PHP验证签名的有效性和权限,并输出是否可以下载的HTTP头。如果可以下载,剩下的事情就是Nginx和浏览器或下载软件之间的事情了。下载效率和纯静态文件相同。

使用的技术原理就是下载请求交给PHP后,PHP返回重写的HTTP头给Nginx,Nginx重新定位到物理文件进行下载。不得瑟了,说道这里大家都懂得了。直接上代码。

nginx configur file

01 server
02 {
03     listen        80 ;
04     server_name dl.yuenshui.com ;
05     root /data0/www/dl.yuenshui.com/ ;
06     location / {
07         rewrite ^/(.*) /dl.php?file= $1 last ;
08     }
09     location /file/ {
10         internal ;
11     }
12     location ~ * .*\.(php)? $ {
13         fastcgi_pass   127.0.0.1 : 9000 ;
14         fastcgi_index index.php ;
15         include fcgi.conf ;
16     }
17 }

dl.php

1 <?php
2 if( ! empty( $_GET [ 'sign' ]) && trim( $_GET [ 'sign' ]) == 'ok') {
3     header( "Content-Type: application/octet-stream");
4     header( "X-Accel-Redirect: /file/" . $_GET [ 'file' ]);
5 }
6 else {
7     header( "http/1.1 403 Forbidden");
8 }
9 ?>

不只是Nginx支持,其他常见Web Server都支持,只是名称不同。如下:
nginx: X-Accel-Redirect
squid: X-Accelerator-Vary
apache: X-Sendfile
lighttpd: X-Sendfile/X-LIGHTTPD-send-file 

上面的配置和验证仅仅为了展示原理。如果用于生产环境需要更多的配置和程序。
如果有技术疑问,欢迎留言。


在WEB架构中经常会遇到私密文件下载服务,比如网盘、OA、邮箱等等。但是大文件用动态语言来验证权限并输出是非常消耗性能的事情。今天用Nginx+PHP实现了简单的文件签名下载服务(感谢同事Toad的辛苦调试)。

原理就是业务给用户的下载地址是真实文件的下载地址加上授权签名(Signature),当用户的下载请求到服务器时,用PHP验证签名的有效性和权限,并输出是否可以下载的HTTP头。如果可以下载,剩下的事情就是Nginx和浏览器或下载软件之间的事情了。下载效率和纯静态文件相同。

使用的技术原理就是下载请求交给PHP后,PHP返回重写的HTTP头给Nginx,Nginx重新定位到物理文件进行下载。不得瑟了,说道这里大家都懂得了。直接上代码。

nginx configur file

01 server
02 {
03     listen        80 ;
04     server_name dl.yuenshui.com ;
05     root /data0/www/dl.yuenshui.com/ ;
06     location / {
07         rewrite ^/(.*) /dl.php?file= $1 last ;
08     }
09     location /file/ {
10         internal ;
11     }
12     location ~ * .*\.(php)? $ {
13         fastcgi_pass   127.0.0.1 : 9000 ;
14         fastcgi_index index.php ;
15         include fcgi.conf ;
16     }
17 }

dl.php

1 <?php
2 if( ! empty( $_GET [ 'sign' ]) && trim( $_GET [ 'sign' ]) == 'ok') {
3     header( "Content-Type: application/octet-stream");
4     header( "X-Accel-Redirect: /file/" . $_GET [ 'file' ]);
5 }
6 else {
7     header( "http/1.1 403 Forbidden");
8 }
9 ?>

不只是Nginx支持,其他常见Web Server都支持,只是名称不同。如下:
nginx: X-Accel-Redirect
squid: X-Accelerator-Vary
apache: X-Sendfile
lighttpd: X-Sendfile/X-LIGHTTPD-send-file 

上面的配置和验证仅仅为了展示原理。如果用于生产环境需要更多的配置和程序。
如果有技术疑问,欢迎留言。

在Nginx中,常见的技巧是后端fastcgi使用X-Accel-Redirect Header来通知nginx使用sendfile加速
文件的输出,避免后端重复读取文件.
当使用X-Accel-Redirect时, 后端输出的一些自定义header就被忽略掉了. 在upstream redirect到
内部location uri后, 这些header并没有被同时输出.

这2天,为了快速解决PHP的性能问题, 就用Perl做的一个Plack application, 用于dispatch MongoDB中的gridfs. 为了加速, 对于gridfs的文件
进行了cache, 并根据文件的md5值输出etag. 这样,当client 请求时, 如果mongodb中的主文件的md5
并没有改变,那么就直接输出304. 否则使用X-Accel-Redirect返回cache file的uri.
使用ETag的原因就是cache文件需要频繁清理, 仅仅依靠last modified time并不可靠.
, Plack的应用也是可以负载到不同的fastcgi upstream.
ETag的验证机制则可以很好的解决这些问题.
因为有很多文件其实很少读取的, 即便某些用户偶然访问, 那么通过ETag如果验证文件在MongoDB中
并没有变动,则直接返回304即可, 无需再重新创建cache文件,从而可以保持cache的热度.

如何能够输出ETag呢, 解决方式就是在X-Accel-Redirect 定向到的location中,使用add_header来
设置附加的ETag header:

location /__file_result__ {
internal;
root /cache;
add_header ETag $upstream_http_etag;
}

这样, 对应的X-Accel-Redirect的文件就有了对应的ETag header.
实际上, 你可以使用add_header加入其他更多的HEADER. 对应的$upstream_http_(header_name).
变量的格式可以在手册的upstream模块部分查到.

BTW, Plack 的效率和性能还是很爽的. 开发效率很高, 10几分钟就能完成一个高效的fastcgi应用.
相对以前手工写, 进步了一大块. 尤其是当你只是想解决几个小问题的时候,相比之下,Cgi::Application都像牛刀了.


你可能感兴趣的:(NGINX文件签名下载验证服务)