CDN的带权限分发对控制成本、防止盗链非常有用,那在Nginx上怎么实现相同的带权限分发呢?
之前有个带权限验证的CDN服务,没有同步开通海外CDN,对海外一直用一台海外的服务器提供文件服务。为了实现和CDN一样的权限算法,是用Node做的服务器。JS做权限验证逻辑当然是非常轻松的,但是Node做文件服务就有点力不从心了,读文件流,写http流,零拷贝不知道怎么实现,似乎还有定位不出来的内存泄漏之类的问题,服务过几天就莫名其妙的会死一下,还要写个监控把它拉起来。
其实一直知道Nginx+Lua可能是最佳解决方案了,不过想想专门去学习一个在其他场合好像也没啥用处的语言……还是让Node服务去重启吧。
直到发现了NginScript(NJS),这就很适合用javascript写着这种无状态的计算服务嵌入到文件分发流程中了。
安装最新的Nginx+NJS,服务器是CentOS 8.2,选择了相应的rpm包:
1 2
| rpm -Uvh https: rpm -Uvh https:
|
文件一直是在COS里面管理,通过COS分发到CDN的,最简单的让Nginx分发COS的方式是用COSFS把bucket挂载到/mnt目录下。不过8.0+的CentOS系统用yum安装COSFS的时候不兼容,要下载编译:
1 2 3 4 5 6 7 8
| yum install automake gcc-c++ git libcurl-devel libxml2-devel fuse-devel make openssl-devel fuse git clone https: cd /usr/cosfs ./autogen.sh ./configure make sudo make install cosfs --version
|
github访问经常连不上,换了加速站点 github.com.cnpmjs.org 才顺利下载到。
因为要对文件访问做权限,所以挂载了COS的目录somewhere不能直接在Nginx里对外开放,要对内开放,只允许经过验证的请求用内部重定向的方式下载:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| load_module modules/ngx_http_js_module.so; ...... http { ...... js_import http.js; ......
location /somewhere{ internal; root /mnt/somewhere/; }
location / { js_content http.redirect; }
|
这样外部来的http请求直接到http.js里面去鉴权通过了才能去访问到somewhere:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| function redirect(r) { for(var i in headers){ r.headersOut[i]=headers[i]; }
if(r.method == "OPTIONS"){ r.return(200, "ok"); }else{ var pathname = r.uri; if(/\.((jpe?g)|(png)|(gif)|(ico)|(html?))$/.test(r.uri)){ r.internalRedirect('/somewhere'+r.uri); }else if(r.args && r.args.data){ var checkResult = checkAuth(decodeURIComponent(r.args.data)); if(checkResult){ r.internalRedirect('/somewhere'+checkResult); }else{ r.internalRedirect('/somewhere/404.html'); } }else{ r.internalRedirect('/somewhere/404.html'); } } }
export default {redirect};
|
鉴权算法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| function checkAuth(fullpath){ const crypto = require('crypto'), rFullPath = /https:\/\/www.myDomainName.com([^\.]*\.mp3)\?sign=([\d]+)\-([0-9a-z]+)\-([0-9]+)\-([0-9a-f]+)/ ; cdnkey = "my-cdn-key..................."; if(!rFullPath.test(fullpath)){ return false } ; var t=fullpath.match(rFullPath); var path=t[1],timestamp=t[2],rand=t[3],uid=t[4],md5=t[5] var timeDiff = Date.now()-timestamp; if(timeDiff>3600000){ return false } var data = path+"-"+timestamp+"-"+rand+"-"+uid+"-"+cdnkey; if (md5 == crypto.createHash('md5').update(data).digest("hex")){ return decodeURI(path) }; return false; }
|
天下JS是一家,代码逻辑大部分从Node服务程序里面抠出来就直接可以用了。
就这样用NginScript简单复刻了腾讯云CDN的带校验静态文件分发。