文/李信棟
前幾天隨手寫的 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 來講,使用空值和主機名都只能知足部分需求。
一個優化方式是組合,當一種方式不能突破即採用另外一種方式。
這種方式的有點在於擴大了適用面積,而且方法對任何場景比較通用。
一個優化方式是接口請求參數帶源引用鏈接。這種方式對不少人來講不太通用,由於不少場景下並不清楚源引用鏈接在哪。可是對個人插件來講很是適用,插件自己保留了源引用。所以能夠很好的繞過防盜鏈限制。