Aliyun OSS Nginx proxy module(阿里云OSS Nginx 签名代理模块)

转载.

1、此文章主要介绍内容
本文主要介绍如何利用Nginx lua 实现将阿里云OSS存储空间做到同本地磁盘一样使用。核心是利用Nginx lua 对OSS请求进行签名并利用内部跳转将所有访问本地Nginx的请求加上OSS 签名转发给OSS,实现本地Nginx无缝衔接阿里云OSS,存储空间无限扩展,存储成本无限下降,数据安全%99.99...... 。

2、本篇文章使用到的一些工具技术及如何学习和获取
1、lua

本文用到的都是一些基本的lua,基本上花半小时阅读下lua的语法就可以轻松理解本文内容

2、Nginx lua

主要是学习nginx lua 及环境部署,不过阅读本文还不需要亲自动手去学习及部署nginx lua 环境,读者可以从docker 官方镜像源pull openresty 镜像进行实验。本文已openresty/1.7.7.2 作为实验环境。

3、阿里云OSS

赶紧开通吧,用起来相当爽,一处存储全球无限制访问
https://www.aliyun.com/act/aliyun/ossdoc.html

4、参考博客

建议阅读我的另外一篇博客,对深入理解OSS 及互联网上提供的HTTP服务有更深刻的理解
https://yq.aliyun.com/articles/7511?spm=0.0.0.0.IWbHSR

3、利用Nginx lua 实现请求签名并转发至OSS
Lua 签名 code
注:此代码并非出自作者之手
oss_auth.lua

	-- has been sorted in alphabetical order
	local signed_subresources = {
	   'acl',
	   'append',
	   'bucketInfo',
	   'cname',
	   'commitTransition',
	   'comp',
	   'cors',
	   'delete',
	   'lifecycle',
	   'location',
	   'logging',
	   'mime',
	   'notification',
	   'objectInfo',
	   'objectMeta',
	   'partData',
	   'partInfo',
	   'partNumber',
	   'policy',
	   'position',
	   'referer',
	   'replication',
	   'replicationLocation',
	   'replicationProgress',
	   'requestPayment',
	   'response-cache-control',
	   'response-content-disposition',
	   'response-content-encoding',
	   'response-content-language',
	   'response-content-type',
	   'response-expires',
	   'restore',
	   'security-token',
	   'tagging',
	   'torrent',
	   'uploadId',
	   'uploads',
	   'versionId',
	   'versioning',
	   'versions',
	   'website'
	}

	function string.startswith(s, start)
	   return string.sub(s, 1, string.len(start)) == start
	end

	local function get_canon_sub_resource()
	   local args = ngx.req.get_uri_args()
	   -- lower keys
	   local keys = {}
	   for k, v in pairs(args) do
	      keys[k:lower()] = v
	   end
	   -- make resource string
	   local s = ''
	   local sep = '?'
	   for i, k in ipairs(signed_subresources) do
	      v = keys[k]
	      if v then
	         -- sub table
	         v = type(v) == 'table' and v[1] or v
	         s = s .. string.format("%s%s=%s", sep, k, v)
	         sep = '&'
	      end
	   end
	   return s
	end

	local function get_canon_resource()
	   resource = ''
	   object = ngx.unescape_uri(ngx.var.uri)
	   sub = get_canon_sub_resource()   
	   return string.format("/%s%s%s", ngx.var.oss_bucket, object, sub)
	end   

	local function get_canon_headers()
	   -- default: <lowerkey, value>
	   local headers = ngx.req.get_headers()
	   local keys = {}
	   for k, v in pairs(headers) do
	      if string.startswith(k, 'x-oss-') then
	         -- client must assemble the same header keys
	         if type(v) ~= 'string' then return nil end
	         table.insert(keys, k)
	      end
	   end
	   -- sorted in alphabetical order
	   table.sort(keys)
	   for i, key in ipairs(keys) do
	      keys[i] = key .. ':' .. headers[key] .. '
'
	   end
	   return table.concat(keys)
	end

	local function calc_sign(key, method, md5, type_, date, oss_headers, resource)
	    -- string_to_sign:
	    -- method + '
' + content_md5 + '
' + content_type + '
'
	    -- + date + '
' + canonicalized_oss_headers + canonicalized_resource
	    local sign_str = string.format('%s
%s
%s
%s
%s%s',
	    method, md5, type_,
	    date, oss_headers, resource)
	    ngx.log(ngx.ERR, "SignStr:", sign_str, "
")
	    local sign_result = ngx.encode_base64(ngx.hmac_sha1(key, sign_str))
	    return sign_result, sign_str
	end   

	local function oss_auth()
	   -- ngx.log(ngx.INFO, 'auth')
	   --local method = ngx.var.request_method
	   local method = ngx.req.get_method()
	   local content_md5 = ngx.var.http_content_md5 or ''
	   local content_type = ngx.var.http_content_type or ''
	   -- get date
	   local date = ngx.var.http_x_oss_date or ngx.var.http_date or ''
	   if date == '' then
	      date = ngx.http_time(ngx.time())
	      -- ngx.log(ngx.INFO, 'Date:', date)
	      ngx.req.set_header('Date', date)
	   end
	   local resource = get_canon_resource()
	   local canon_headers = get_canon_headers()
	   local sign_result, sign_str = calc_sign(ngx.var.oss_auth_key, method, content_md5,
	   content_type, date, canon_headers, resource)
	   -- ngx.log(ngx.INFO, 'sign string:', sign_str)
	   -- ngx.log(ngx.INFO, 'sign string len:', string.len(sign_str))
	   local auth = string.format("OSS %s:%s", ngx.var.oss_auth_id, sign_result)
	   ngx.req.set_header('Authorization', auth)
	   ngx.exec("@oss")
	end   

	-- main
	res = oss_auth()

	if res then
	   ngx.exit(res)
	end

nginx.conf

	server {
	    listen 8000;

	    proxy_http_version 1.1;
	    proxy_buffering off;
	    proxy_request_buffering off;

	    location / {
	        set $oss_bucket "your_oss_bucket";
	        set $oss_auth_id "your_access_id";
	        set $oss_auth_key "your_access_key";
	        rewrite_by_lua_file "/path/oss_auth.lua";
	    }

	    # internal redirect
	    location @oss {
	        // endpoint eg: oss.aliyuncs.com
	        proxy_pass http://your_oss_bucket.endpoint; 
	    }
	}

、如何使用上述代码
首先oss_auth.lua 无需做任何改动

nginx.conf 中需要将
your_oss_bucket 替换为阿里云OSS 的bucket名
your_access_id替换未AccessKeyId
your_access_key 替换为 AccessKeySecret

例如:

	error_log  logs/error.log  debug;
	events {
	    worker_connections  1024;
	}
	http {
	    include       mime.types;
	    lua_package_path "/usr/servers/lualib/?.lua;";
	    lua_package_cpath "/usr/servers/lualib/?.so;"; 
	    server {
	        listen       80;
	        location / {
	            set $oss_bucket "bucket-example";
	            set $oss_auth_id "za2127hbbdsdjhkhskel0ytocbzr";
	            set $oss_auth_key "gMOG3o+JHDSJHCNMcsaH+Q=";
	            rewrite_by_lua_file conf/lua/oss_auth.lua;
	        }

	        location @oss {
	            proxy_pass http://bucket-example.oss-cn-qingdao.aliyuncs.com;
	        }
	    }
	}
原文地址:https://www.cnblogs.com/gorgage/p/7410453.html