使用OpenResty进行图片代理(破解反盗链),openresty图片


  • lua-resty-http
    • http_dns.lua
    • proxy.lua
    • header_filter.lua

今天在使用scrapy爬取网站的时候,因为网站的容量有限,不能拉取图片进行存储。但是源站的图片又存在着反盗链的限制,所以利用OpenResty做了一个图片代理的接口,实现了破解反盗链的功能。(如果你要问我为什么使用OpenResty来做,我会告诉你因为网站就是用OpenResty开发的)

lua-resty-http

在OpenResty上面使用代理时,一般情况下会用proxy_pass或者lua-resty-http。当我们使用上面2种方式去实现的时候,会发现其不会解析域名的信息,从而造成无法发起http请求。这里面介绍一下使用动态DNS解析域名信息,然后发起HTTP请求。

首先需要在OpenResty安装目录下面添加lua-resty-http的依赖库:http.luahttp_headers.lua。将这2个文件放在resty下面即可。

下面介绍一下具体的实现,首先提供一个工具类

http_dns.lua

local http = require "resty.http"
local resolver = require "resty.dns.resolver"

local _M = {}

_M._VERSION="0.1"

function _M:http_request_with_dns( url, param )
    -- get domain
    local domain = ngx.re.match(url, [[//([\S]+?)/]])
    domain = (domain and 1 == #domain and domain[1]) or nil
    if not domain then
        ngx.log(ngx.ERR, "get the domain fail from url:", url)
        return {status=ngx.HTTP_BAD_REQUEST}
    end

    -- add param
    if not param.headers then
        param.headers = {}
    end
    param.headers.Host = domain

    -- get domain ip
    local domain_ip, err = self:get_domain_ip_by_dns(domain)
    if not domain_ip then
        ngx.log(ngx.ERR, "get the domain[", domain ,"] ip by dns failed:", err)
        return {status=ngx.HTTP_SERVICE_UNAVAILABLE}
    end

    -- http request
    local httpc = http.new()
    local temp_url = ngx.re.gsub(url, "//"..domain.."/", string.format("//%s/", domain_ip))

    local res, err = httpc:request_uri(temp_url, param)
    if err then
        return {status=ngx.HTTP_SERVICE_UNAVAILABLE}
    end

    -- httpc:request_uri 内部已经调用了keepalive,默认支持长连接
    -- httpc:set_keepalive(1000, 100)
    return res
end


-- 根据域名获取IP地址
function _M:get_domain_ip_by_dns( domain )
  -- 这里写死了google的域名服务ip,要根据实际情况做调整(例如放到指定配置或数据库中)
  local dns = "8.8.8.8"

  local r, err = resolver:new{
      nameservers = {dns, {dns, 53} },
      retrans = 5,  -- 5 retransmissions on receive timeout
      timeout = 2000,  -- 2 sec
  }

  if not r then
      return nil, "failed to instantiate the resolver: " .. err
  end

  local answers, err = r:query(domain)
  if not answers then
      return nil, "failed to query the DNS server: " .. err
  end

  if answers.errcode then
      return nil, "server returned error code: " .. answers.errcode .. ": " .. answers.errstr
  end

  for i, ans in ipairs(answers) do
    if ans.address then
      return ans.address
    end
  end

  return nil, "not founded"
end

return _M

上面提供了请求的入口,提供一个URL和param参数即可。

下面提供了获取图片的HTTP接口,用以提供给自己的网站使用。

proxy.lua

local req = require "dispatch.req"
local httpdns = require "libs.http_dns"

local _M = {}

_M._VERSION="0.1"

-- 获取类型列表
function _M:picture()
    local args = req.getArgs()
    local imgUrl = args['imgUrl']

    local param = {}
    param['method'] = "GET"
    param['headers'] = {
                ['User-Agent'] = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36',
                ['Accept'] = 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
                ['Accept-Language'] = 'zh-CN,zh;q=0.9',
                ['Referer'] = imgUrl
            }
    local res = httpdns:http_request_with_dns(imgUrl, param)

    ngx.say(res.body)
end

return _M

到这里图片代理接口的工作就结束了,下面还需要对响应头做一些设定。

header_filter_by_lua_file lua/filter/header_filter.lua;

header_filter.lua

-- 设置响应头信息
local uri = ngx.var.request_uri

local result = string.find(uri, "example.com") -- 发现代理链接,则更改响应头信息
if result == nil then -- 普通请求
    ngx.header["Content-Type"] = "text/html;charset=UTF-8"
else -- 代理请求
    ngx.header["Content-Type"] = "image/jpeg"
end

最后在页面端使用如下方式就可以使用代理接口了。

/proxy/picture?imgUrl=http://image.example.com/image.jpg

参考:《OpenResty最佳实践》
链接:http://moguhu.com/article/detail?articleId=82

相关内容

    暂无相关文章