zl程序教程

您现在的位置是:首页 >  其他

当前栏目

用NginScript做个带权限的文件分发服务

2023-03-14 22:31:21 时间

之前有个带权限验证的CDN服务,没有同步开通海外CDN,一直用一台香港的服务器提供文件服务。为了实现和CDN一样的权限算法,是用Node做的服务器。JS做权限验证逻辑当然是非常轻松的,但是Node做文件服务就有点力不从心了,读文件流,写http流,零拷贝不知道怎么实现,似乎还有定位不出来的内存泄漏之类的问题,服务过几天就莫名其妙的会死一下,还要写个监控把它拉起来。

其实一直知道Nginx+Lua可能是最佳解决方案了,不过想想专门去学习一个在其他场合好像也没啥用处的语言……还是让Node服务去重启吧。

直到发现了NginScript(NJS),这就很适合用javascript写着这种无状态的计算服务嵌入到文件分发流程中了。

安装最新的Nginx+NJS,服务器是CentOS 8.2,选择了相应的rpm包:

rpm -Uvh https://nginx.org/packages/centos/8/x86_64/RPMS/nginx-1.20.1-1.el8.ngx.x86_64.rpm
rpm -Uvh https://nginx.org/packages/centos/8/x86_64/RPMS/nginx-module-njs-1.20.1%2B0.7.0-1.el8.ngx.x86_64.rpm

文件一直是在COS里面管理,通过COS分发到CDN的,最简单的让Nginx分发COS的方式是用COSFS把bucket挂载到/mnt目录下。不过8.0+的CentOS系统用yum安装COSFS的时候不兼容,要下载编译:

 yum install automake gcc-c++ git libcurl-devel libxml2-devel fuse-devel make openssl-devel fuse
git clone https://github.com.cnpmjs.org/tencentyun/cosfs /usr/cosfs
cd /usr/cosfs
./autogen.sh
./configure
make
sudo make install
cosfs --version

github访问经常连不上,换了加速站点 github.com.cnpmjs.org 才顺利下载到。

因为要对文件访问做权限,所以挂载了COS的目录somewhere不能直接在Nginx里对外开放,要对内开放,只允许经过验证的请求用内部重定向的方式下载:

    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:

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};

鉴权算法:

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的带校验静态文件分发。