文/李信栋
前几天随手写的 chrome 插件遇到了防盗链问题,因为插件不能用 js iframe 的方法反防盗链,因而想用服务器作个中转。记录一下上手项目的各个点,之后再用 nodejs 就不用处处查资料了。javascript
以前没有一套特别熟悉的 web 开发框架,加上插件存储服务依赖的平台 LeanCloud 恰好支持部署 nodejs 网站,恰好拿这个小项目做为 nodejs 上手项目。java
怎么「破解防盗链」呢?
想要破解,就得先知道目标——防盗链如何实现。
大多数站点的策略很简单: 判断request请求头的refer是否来源于本站。若不是,拒绝访问真实图片。而咱们知道: 请求头是来自于客户端,是可伪造的。node
思路
那么,咱们伪造一个正确的refer来访问不就好了?
整个业务逻辑大概像这样:git
这就起到了图片中转的做用。
github
主要是 http.createServer().listen(port) 这组方法,创建服务器、监听端口一键搞定。web
var http = require('http');
http.createServer(function (request, response) {
// do things here
}).listen(8888);
console.log('Server running at: 8888');复制代码
createServer 回调方法的两个参数 req res 是 http request 和 response 的内容,打印一下他们的内容。
request 是 InComingMessage 类,打印它的 url 字段。chrome
var http = require('http');
var url = require('url');
var util = require('util');
http.createServer(function(req, res){
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end(util.inspect(url.parse(req.url, true)));
}).listen(3000);复制代码
请求
http://localhost:3000/api?url=http://abc.com/image.pngshell
请求结果express
Url {
protocol: null,
slashes: null,
auth: null,
host: null,
port: null,
hostname: null,
hash: null,
search: '?url=http://abc.com/image.png',
query: { url: 'http://abc.com/image.png' },
pathname: '/api',
path: '/api?url=http://abc.com/image.png',
href: '/api?url=http://abc.com/image.png' }复制代码
query 字段恰好是咱们想要的内容,下载这个字段对应的图片。api
request 模块支持管道方法,能够和 shell 的管道同样理解。
这能够省不少事,不须要在本地存储图片,不须要处理杂七杂八的事情,甚至不须要再去了解 nodejs 的流。一个方法全搞定。
关键方法: request(options).pipe(res)
var options = {
uri: imgUrl, // 这个 uri 为空时,会认为该字段不存在,报异常
headers: {
'Referer': referrer // 解决部分防盗链选项
}
};
request(options).pipe(res);复制代码
完整代码
'use strict';
var router = require('express').Router();
var http = require('http');
var url = require('url');
var util = require('util');
var fs = require('fs');
var callfile = require('child_process');
var request = require('request');
router.get('/', function(req, res, next) {
var imgUrl = url.parse(req.url, true).query.url;
console.log(url.parse(req.url,true).query);
console.log('get a request for ' + imgUrl);
if (imgUrl == null || imgUrl == "" || imgUrl == undefined) {
console.log('end');
res.end();
return;
}
var parsedUrl = url.parse(imgUrl);
// 这里暂时使用图片服务器主机名作Referer
var referrer = parsedUrl.protocol + '//' + parsedUrl.host;
console.log('referrer ' + referrer);
var options = {
uri: imgUrl,
headers: {
'Referer': referrer
}
};
function callback(error, response, body) {
if (!error && response.statusCode == 200) {
console.log("type " + response.headers['content-type']);
}
res.end(response.body);
}
// request(options, callback);
request(options)
.on('error', function(err) {
console.log(err)
})
.pipe(res);
});
module.exports = router;复制代码
这部分主要是防盗链部分的优化。
单就 Referer 来讲,使用空值和主机名都只能知足部分需求。
一个优化方式是组合,当一种方式不能突破即采用另外一种方式。
这种方式的有点在于扩大了适用面积,而且方法对任何场景比较通用。
一个优化方式是接口请求参数带源引用链接。这种方式对不少人来讲不太通用,由于不少场景下并不清楚源引用链接在哪。可是对个人插件来讲很是适用,插件自己保留了源引用。所以能够很好的绕过防盗链限制。