nginx X-Accel-Redirect实现文件下载权限控制

May 12th, 2009 no comment

对文件下载的权限进行精确控制在很多地方都需要,例如有偿的下载服务、网络硬盘、个人相册、防止本站内容被外站盗链等。

已知的一些方法有以下几种:

0,通过webserver的rewrite来经常改变下载文件的URL;

1,对HTTP请求的referer头进行判断,要求其必须来自指定的URL;

2,由cgi程序来做权限检查,然后open文件,返回给client;

3,就是本文所说的使用nginx X-Accel-Redirect方法了(lightty也有对应的X-sendfile来完成相同的功能)。

方法0的问题是URL经常变化,且为了防止URL被猜到,要经常的插入一些随机的长字符,造成URL冗长,非常不优雅;方法1是不可靠的,因为 Referer头是由client发出的,完全可以伪造;方法2,因为是由cgi程序打开并读取文件,所以在下载过程中需要持续占用一个cgi进程(线程),并且由于cgi的脚本语言IO速度一般比较慢,导致其总体效率低下。

方法3是我目前认为比较 优雅 的方式 全面 解决此问题的方法。

整个的过程如下图所示:


解释一下整个过程:

步骤0,client请求http://downloaddomain.com/download/my.iso,此请求被CGI程序解析(对于 nginx应该是fastcgi)。

步骤1,CGI程序根据访问者的身份和所请求的资源其是否有下载权限来判定是否有打开的权限。如果有,那么根据此请求得到对应文件的磁盘存放路径,例如是 /var/data/my.iso。那么程序返回时在HTTP header加入X-Accel-Redirect: /protectfile/data/my.iso,并加上head Content-Type:application/octet-stream。

步骤2,nginx得到cgi程序的回应后发现带有X-Accel-Redirect的header,那么根据这个头记录的路径信息打开磁盘文件。

步骤3,nginx把打开文件的内容返回给client端。

这样所有的权限检查都可以在步骤1内完成,而且cgi返回带X-Accel-Redirect的头后,其执行已经终止,剩下的传输文件的工作由nginx 来接管,同时X-Accel-Redirect头的信息被nginx删除,也隐藏了文件实际存储目录,并且由于nginx在打开静态文件上使用了 sendfile(2),其IO效率非常高。

具体如何来做?很简单!

在nginx中的相应的虚拟主机中加入:

location ^~ /protectfile/ {
internal;
alias /var/download/;
add_header Cache-Control no-cache, no-store;
}

然后在cgi程序中加入content-type和x-accel-redirect头,以django为例:

def handle_download(request, filename):

# do some policy control check

… …

response = HttpResponse()

response[‘Content-Type’] = “application/octet-stream”

response[‘X-Accel-Redirect’] = <filepath of the filename>
return response

Done.

Comments are closed.

Leave A Comment