服務器做防盜鏈圖片中轉,Node.js 上手項目簡明教程

文/李信棟
前幾天隨手寫的 chrome 插件遇到了防盜鏈問題,因爲插件不能用 js iframe 的方法反防盜鏈,因而想用服務器作箇中轉。記錄一下上手項目的各個點,之後再用 nodejs 就不用處處查資料了。javascript

以前沒有一套特別熟悉的 web 開發框架,加上插件存儲服務依賴的平臺 LeanCloud 恰好支持部署 nodejs 網站,恰好拿這個小項目做爲 nodejs 上手項目。java

怎麼「破解防盜鏈」呢?
想要破解,就得先知道目標——防盜鏈如何實現。
大多數站點的策略很簡單: 判斷request請求頭的refer是否來源於本站。若不是,拒絕訪問真實圖片。而咱們知道: 請求頭是來自於客戶端,是可僞造的。node

思路
那麼,咱們僞造一個正確的refer來訪問不就好了?
整個業務邏輯大概像這樣:git

  • 本身的服務器後臺接受帶目標圖片url參數的請求
  • 僞造refer請求目標圖片
  • 把請求到的數據做爲response返回

這就起到了圖片中轉的做用。

github

1. 項目是什麼樣子

1.1 接口的樣子?

1.2 應該怎麼作?

  • 把服務器跑起來
  • 處理 GET 請求
  • 分析請求參數
  • 下載原圖
  • response 原圖

2. 學習路徑(在對目標未知的前提下提出疑問)

  • 如何開始,創建服務器
  • 如何處理基本請求 GET POST
  • 如何下載圖片並轉發
  • 完成基本功能,上線
  • 優化

2.1 如何開始,創建服務器

主要是 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');複製代碼

2.2 如何處理基本請求 GET POST

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

2.3 如何下載圖片並轉發

request 模塊支持管道方法,能夠和 shell 的管道同樣理解。

這能夠省不少事,不須要在本地存儲圖片,不須要處理雜七雜八的事情,甚至不須要再去了解 nodejs 的流。一個方法全搞定。

關鍵方法: request(options).pipe(res)

var options = {
      uri: imgUrl, // 這個 uri 爲空時,會認爲該字段不存在,報異常
      headers: {
         'Referer': referrer // 解決部分防盜鏈選項
      }
    };
    request(options).pipe(res);複製代碼

2.4 完成基本功能,上線

項目地址

完整代碼

 '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;複製代碼

2.5 優化

這部分主要是防盜鏈部分的優化。

單就 Referer 來講,使用空值和主機名都只能知足部分需求。

一個優化方式是組合,當一種方式不能突破即採用另外一種方式。
這種方式的有點在於擴大了適用面積,而且方法對任何場景比較通用。

一個優化方式是接口請求參數帶源引用鏈接。這種方式對不少人來講不太通用,由於不少場景下並不清楚源引用鏈接在哪。可是對個人插件來講很是適用,插件自己保留了源引用。所以能夠很好的繞過防盜鏈限制。

相關文章
相關標籤/搜索