成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

通過 nginx-lua 給 Nginx 增加 OAuth 支持

awesome23 / 2105人閱讀

摘要:我的提供者類使用了來發(fā)送認(rèn)證的錯(cuò)誤信息,我們也需要在我們的腳本中為其提供支持現(xiàn)在我們解決了基本的錯(cuò)誤情況,我們要為訪問令牌設(shè)置?,F(xiàn)在我們只需要通過一些請(qǐng)求頭信息告知我們當(dāng)前的應(yīng)用誰登錄了就行了。

前言:我們使用Nginx的Lua中間件建立了OAuth2認(rèn)證和授權(quán)層。如果你也有此打算,閱讀下面的文檔,實(shí)現(xiàn)自動(dòng)化并獲得收益。
SeatGeek 在過去幾年中取得了發(fā)展,我們已經(jīng)積累了不少針對(duì)各種任務(wù)的不同管理接口。我們通常為新的展示需求創(chuàng)建新模塊,比如我們自己的博客、圖表等。我們還定期開發(fā)內(nèi)部工具來處理諸如部署、可視化操作及事件處理等事務(wù)。在處理這些事務(wù)中,我們使用了幾個(gè)不同的接口來認(rèn)證:

Github/Google Oauth

我們SeatGeek內(nèi)部的用戶系統(tǒng)

基本認(rèn)證

硬編碼登錄

顯然,實(shí)際應(yīng)用中很不規(guī)范。多個(gè)認(rèn)證系統(tǒng)使得難以對(duì)用于訪問級(jí)別和通用許可的各種數(shù)據(jù)庫進(jìn)行抽象。

單系統(tǒng)認(rèn)證

我們也做了一些關(guān)于如何設(shè)置將解決我們問題的研究。這促使了Odin的出現(xiàn),它在驗(yàn)證谷歌應(yīng)用的用戶方面工作的很好。不幸的是它需要使用Apache,而我們已和Nginx結(jié)為連理并把它作為我們的后端應(yīng)用的前端。
幸運(yùn)的是,我看了mixlr的博客并引用了他們Lua在Nginx上的應(yīng)用:

修改響應(yīng)頭

重寫內(nèi)部請(qǐng)求

選擇性地基于IP拒絕主機(jī)訪問

最后一條看起來很有趣。它開啟了軟件包管理的地獄之旅。

構(gòu)建支持Lua的Nginx

Lua for Nginx沒有被包含在Nginx的核心中,我們經(jīng)常要為OSX構(gòu)建Nginx用于開發(fā)測(cè)試,為Linux構(gòu)建用于部署。

為OSX定制Nginx

對(duì)于OSX系統(tǒng),我推薦使用Homebrew進(jìn)行包管理。它初始的Nginx安裝包啟用的模塊不多,這有非常好的理由:

  

關(guān)鍵在于NGINX有著如此之多的選項(xiàng),如果把它們都加入初始包那一定是瘋了,如果我們只把其中一些加入其中就會(huì)迫使我們把所有都加入,這會(huì)讓我們瘋掉的。
--- Charlie Sharpsteen, @sharpie

所以我們需要自己構(gòu)建。合理地構(gòu)建Nginx可以方便我們以后繼續(xù)擴(kuò)展。幸運(yùn)的是,使用Homebrew進(jìn)行包管理十分方便快捷。
我們首先需要一個(gè)工作空間:

cd ~
mkdir -p src
cd src

之后,我們需要找到初始安裝信息包。你可以通過下面任何一種方式得到它:

找到HOMEBREW_PREFIX目錄,通常在/usr/local下,在其中找到nginx.rb文件

從下列地址取得https://raw.github.com/mxcl/homebrew/master/Library/Formula/nginx.rb

使用如下命令 brew cat nginx > nginx.rb

此時(shí)如果我們執(zhí)行 brew install ./nginx.rb 命令, 它會(huì)依據(jù)其中的信息安裝Nginx。既然現(xiàn)在我們要完全定制Nginx,我們要重命名信息包,這樣之后通過 brew update 命令進(jìn)行更新的時(shí)候就不會(huì)覆蓋我們自定義的了:

mv nginx.rb nginx-custom.rb
cat nginx-custom.rb | sed "s/class Nginx/class NginxCustom/" >> tmp
rm nginx-custom.rb
mv tmp nginx-custom.rb

我們現(xiàn)在可以將我們需要的模塊加入安裝信息包中并開始編譯了。這很簡(jiǎn)單,我們只要將所有我們需要的模塊以參數(shù)形式傳給 brew install 命令,代碼如下:

# Collects arguments from ARGV
def collect_modules regex=nil
    ARGV.select { |arg| arg.match(regex) != nil }.collect { |arg| arg.gsub(regex, "") }
end

# Get nginx modules that are not compiled in by default specified in ARGV
def nginx_modules; collect_modules(/^--include-module-/); end

# Get nginx modules that are available on github specified in ARGV
def add_from_github; collect_modules(/^--add-github-module=/); end

# Get nginx modules from mdounin"s hg repository specified in ARGV
def add_from_mdounin; collect_modules(/^--add-mdounin-module=/); end

# Retrieve a repository from github
def fetch_from_github name
    name, repository = name.split("/")
    raise "You must specify a repository name for github modules" if repository.nil?

    puts "- adding #{repository} from github..."
    `git clone -q git://github.com/#{name}/#{repository} modules/#{name}/#{repository}`
    path = Dir.pwd + "/modules/" + name + "/" + repository
end

# Retrieve a tar of a package from mdounin
def fetch_from_mdounin name
    name, hash = name.split("#")
    raise "You must specify a commit sha for mdounin modules" if hash.nil?

    puts "- adding #{name} from mdounin..."
    `mkdir -p modules/mdounin && cd $_ ; curl -s -O http://mdounin.ru/hg/#{name}/archive/#{hash}.tar.gz; tar -zxf #{hash}.tar.gz`
    path = Dir.pwd + "/modules/mdounin/" + name + "-" + hash
end

上面這個(gè)輔助模塊可以讓我們指定想要的模塊并檢索模塊的地址。現(xiàn)在,我們需要修改nginx-custom.rb文件,使之包含這些模塊的名字并在包中檢索它們,在58行附近:

nginx_modules.each { |name| args << "--with-#{name}"; puts "- adding #{name} module" }
add_from_github.each { |name| args <<  "--add-module=#{fetch_from_github(name)}" }
add_from_mdounin.each { |name| args <<  "--add-module=#{fetch_from_mdounin(name)}" }

現(xiàn)在我們可以編譯我們重新定制的nginx了:

brew install ./nginx-custom.rb 
    --add-github-module=agentzh/chunkin-nginx-module 
    --include-module-http_gzip_static_module 
    --add-mdounin-module=ngx_http_auth_request_module#a29d74804ff1

你可以方便地在seatgeek/homebrew-formulae找到以上信息包。

為Debian定制Nginx

我們通常都會(huì)部署到Debian的發(fā)行版-通常是Ubuntu上作為我們的產(chǎn)品服務(wù)器。如果是這樣,那將會(huì)非常簡(jiǎn)單,運(yùn)行 dpkg -i nginx-custom 安裝我們的定制包。這步驟如此簡(jiǎn)單你一運(yùn)行它就完成了。
一些在搜索定制debian/ubuntu包時(shí)的筆記:

你可以通過 apt-get source PACKAGE_NAME來獲取debian安裝包。

Debian安裝包受控于一個(gè) rules文件,你需要sed-fu來操作它。

你可以通過編輯 control 文件來更新 deb包的依賴。注意這里指定了一些元依賴(meta-dependency)你不要去刪除它,但是這些很容易分辨出來。

新的發(fā)布必須要在changelog里注明,否則包有可能不會(huì)被升級(jí)因?yàn)樗赡芤呀?jīng)被安裝過了。你需要在表單里使用 +tag_name來指明哪些是你自己在baseline上新加的改動(dòng)。我會(huì)額外加上一個(gè)數(shù)字-從0開始-指示出包的發(fā)布編號(hào)。

大多數(shù)的改動(dòng)可以以某種方式自動(dòng)更改,但是似乎沒有一個(gè)簡(jiǎn)單的命令行工具可以創(chuàng)建定制的發(fā)布包。這也正是我們感興趣的地方,如果你知道什么的話,請(qǐng)給我們給我們提供一些鏈接,工具或方法。

在運(yùn)行這個(gè)偉大過程的同時(shí),我構(gòu)建了一個(gè)小的批處理腳本來自動(dòng)化這個(gè)過程的主要步驟,你可以在 gist on github: gist.github.com/4126937 上找到它。
在我意識(shí)到這個(gè)過程可以被腳本化之前僅僅花費(fèi)了90個(gè)nginx包的構(gòu)建時(shí)間。

全部OAuth

現(xiàn)在可以測(cè)試并部署嵌入Nginx的Lua腳本了,讓我們開始Lua編程。
nginx-lua模塊提供了一些輔助功能和變量來訪問Nginx的絕大多數(shù)功能,顯然我們可以通過access_by_lua中該模塊提供的指令來強(qiáng)制打開OAuth認(rèn)證。

  

當(dāng)使用*_by_lua_file指令后,必須重載nginx來使其起作用。

我用 NodeJS 為 SeatGeek 創(chuàng)建了一個(gè)簡(jiǎn)單的 OAuth2 提供者類。這部分內(nèi)容很簡(jiǎn)單,你也很容易獲得你是通用語言的響應(yīng)版本。
接下來,我們的OAuth API使用JSON來處理令牌(token)、訪問級(jí)別(access level)和重新認(rèn)證響應(yīng)(re-authentication responses)。所以我們需要安裝lua-cjson模塊。

# install lua-cjson
if [ ! -d lua-cjson-2.1.0 ]; then
    tar zxf lua-cjson-2.1.0.tar.gz
fi
cd lua-cjson-2.1.0
sed "s/i686/x86_64/" /usr/share/lua/5.1/luarocks/config.lua > /usr/share/lua/5.1/luarocks/config.lua-tmp
rm /usr/share/lua/5.1/luarocks/config.lua
mv /usr/share/lua/5.1/luarocks/config.lua-tmp /usr/share/lua/5.1/luarocks/config.lua
luarocks make

我的 OAuth 提供者類使用了 query-string 來發(fā)送認(rèn)證的錯(cuò)誤信息,我們也需要在我們的Lua腳本中為其提供支持:

local args = ngx.req.get_uri_args()
if args.error and args.error == "access_denied" then
    ngx.status = ngx.HTTP_UNAUTHORIZED
    ngx.say("{"status": 401, "message": ""..args.error_description..""}")
    return ngx.exit(ngx.HTTP_OK)
end

現(xiàn)在我們解決了基本的錯(cuò)誤情況,我們要為訪問令牌設(shè)置cookie。在我的例子中,cookie會(huì)在訪問令牌過期前過期,所以我可以利用cookie來刷新訪問令牌。

local access_token = ngx.var.cookie_SGAccessToken
if access_token then
    ngx.header["Set-Cookie"] = "SGAccessToken="..access_token.."; path=/;Max-Age=3000"
end

現(xiàn)在,我們解決了錯(cuò)誤響應(yīng)的api,并儲(chǔ)存了access_token供后續(xù)訪問。我們現(xiàn)在需要確保OAuth認(rèn)證過程正確啟動(dòng)。下面,我們想要:

如果沒有access_token已經(jīng)或?qū)⒁鎯?chǔ),開啟OAuth認(rèn)證

如果query string的參數(shù)中有OAuth訪問代碼(access code),使用OAuth API檢索用戶的access_token

拒絕使用非法訪問代碼用戶的請(qǐng)求

閱讀nginx-Lua函數(shù)和變量的相關(guān)文檔可以解決一些問題,或許還能告訴你訪問特定請(qǐng)求/響應(yīng)信息的各種方法。
此時(shí),我們需要從我們的api接口獲取一個(gè)TOKEN。nginx-lua提供了 ngx.location.capture 方法,支持發(fā)起一個(gè)內(nèi)部請(qǐng)求到redis,并接收響應(yīng)。這意味著,我們不能直接調(diào)用類似于 http://seatgeek.com/ncaa-football-tickets,但我們可以用 proxy_pass 把這種外部鏈接包裝成內(nèi)部請(qǐng)求。
我們通常約定給這樣的內(nèi)部請(qǐng)求前面加一個(gè)_(下劃線), 用來阻止外部直接訪問。

-- 第一步,從api獲取獲取token
if not access_token or args.code then
    if args.code then
        -- internal-oauth:1337/access_token
        local res = ngx.location.capture("/_access_token?client_id="..app_id.."&client_secret="..app_secret.."&code="..args.code)

        -- 終止所有非法請(qǐng)求
        if res.status ~= 200 then
            ngx.status = res.status
            ngx.say(res.body)
            ngx.exit(ngx.HTTP_OK)
        end

        -- 解碼 token
        local text = res.body
        local json = cjson.decode(text)
        access_token = json.access_token
    end

    -- cookie 和 proxy_pass token 請(qǐng)求失敗
    if not access_token then
        -- 跟蹤用戶訪問,用于透明的重定向
        ngx.header["Set-Cookie"] = "SGRedirectBack="..nginx_uri.."; path=/;Max-Age=120"

        -- 重定向到 /oauth , 獲取權(quán)限
        return ngx.redirect("internal-oauth:1337/oauth?client_id="..app_id.."&scope=all")
    end
end

此時(shí)在Lua腳本中,應(yīng)該已經(jīng)有了一個(gè)可用的 access_token。我們可以用來獲取任何請(qǐng)求需要的用戶信息。在本文中,返回401表示沒有權(quán)限,403表示token過期,并且授權(quán)信息用簡(jiǎn)單數(shù)字打包成json響應(yīng)。

-- 確保用戶有訪問web應(yīng)用的權(quán)限
-- internal-oauth:1337/accessible
local res = ngx.location.capture("/_user", {args = { access_token = access_token } } )
if res.status ~= 200 then
    -- 刪除損壞的 token
    ngx.header["Set-Cookie"] = "SGAccessToken=deleted; path=/; Expires=Thu, 01-Jan-1970 00:00:01 GMT"

    -- 如果 token 損壞 ,重定向 403 forbidden 到 oauth
    if res.status == 403 then
        return ngx.redirect("https://seatgeek.com/oauth?client_id="..app_id.."&scope=all")
    end

    -- 沒有權(quán)限
    ngx.status = res.status
    ngx.say("{"status": 503, "message": "Error accessing api/me for credentials"}")
    return ngx.exit(ngx.HTTP_OK)
end

現(xiàn)在,我們已經(jīng)驗(yàn)證了用戶確實(shí)是經(jīng)過身份驗(yàn)證的并且具有某個(gè)級(jí)別的訪問權(quán)限,我們可以檢查他們的訪問級(jí)別,看看是否同我們所定義的任何當(dāng)前端點(diǎn)的訪問級(jí)別有沖突。我個(gè)人在這一步刪除了SGAccessToken,以便用戶擁有使用不同的用戶身份登錄的能力,但這一做法用不用由你決定。

local json = cjson.decode(res.body)
-- Ensure we have the minimum for access_level to this resource
if json.access_level < 255 then
    -- Expire their stored token
    ngx.header["Set-Cookie"] = "SGAccessToken=deleted; path=/; Expires=Thu, 01-Jan-1970 00:00:01 GMT"

    -- Disallow access
    ngx.status = ngx.HTTP_UNAUTHORIZED
    ngx.say("{"status": 403, "message": "USER_ID"..json.user_id.." has no access to this resource"}")
    return ngx.exit(ngx.HTTP_OK)
end

-- Store the access_token within a cookie
ngx.header["Set-Cookie"] = "SGAccessToken="..access_token.."; path=/;Max-Age=3000"

-- Support redirection back to your request if necessary
local redirect_back = ngx.var.cookie_SGRedirectBack
if redirect_back then
    ngx.header["Set-Cookie"] = "SGRedirectBack=deleted; path=/; Expires=Thu, 01-Jan-1970 00:00:01 GMT"
    return ngx.redirect(redirect_back)
end

現(xiàn)在我們只需要通過一些請(qǐng)求頭信息告知我們當(dāng)前的應(yīng)用誰登錄了就行了。您可以重用REMOTE_USER,如果你有需求的話,就可以用這個(gè)取代基本的身份驗(yàn)證,而除此之外的任何事情都是公平的游戲。

-- Set some headers for use within the protected endpoint
ngx.req.set_header("X-USER-ACCESS-LEVEL", json.access_level)
ngx.req.set_header("X-USER-EMAIL", json.email)

我現(xiàn)在就可以像任何其它的站點(diǎn)那樣在我的應(yīng)用程序中訪問這些http頭了,而不是用數(shù)百行代碼和大量的時(shí)間來重新實(shí)現(xiàn)身份驗(yàn)證。

Nginx 和 Lua, 放在樹結(jié)構(gòu)里面

在這一點(diǎn)上,我們應(yīng)該有一個(gè)可以用來阻擋/拒絕訪問的LUA腳本。我們可以將這個(gè)腳本放到磁盤上的一個(gè)文件中,然后使用access_by_lua_file配置來將它應(yīng)用在我們的nginx站點(diǎn)中。在SeatGeek中,我們使用Chief來模板化輸出配置文件,雖然你可以使用Puppet,F(xiàn)abric,或者其它任何你喜歡的工具。

下面是你可以用來使所有東西都運(yùn)行起來的最簡(jiǎn)單的nginx的網(wǎng)站。你也可能會(huì)想要檢查下access.lua - 在這里:gist.github.com/4196901 - 它是上面的lua腳本編譯后的文件。

# The app we are proxying to
upstream production-app {
  server localhost:8080;
}

# The internal oauth provider
upstream internal-oauth {
  server localhost:1337;
}

server {
  listen       80;
  server_name  private.example.com;
  root         /apps;
  charset      utf-8;

  # This will run for everything but subrequests
  access_by_lua_file "/etc/nginx/access.lua";

  # Used in a subrequest
  location /_access_token { proxy_pass http://internal-oauth/oauth/access_token; }
  location /_user { proxy_pass http://internal-oauth/user; }

  location / {
    proxy_set_header  X-Real-IP  $remote_addr;
    proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header  Host $http_host;
    proxy_redirect    off;
    proxy_max_temp_file_size 0;

    if (!-f $request_filename) {
      proxy_pass http://production-app;
      break;
    }
  }

}
進(jìn)一步思考

雖然此設(shè)置運(yùn)行的比較好,但是我想指出一些缺點(diǎn):

上面的代碼是我們access_by_lua腳本的簡(jiǎn)化。我們也處理保存POST提交的請(qǐng)求,JS加入到到頁面更新會(huì)話自動(dòng)處理的令牌更新等,你可能不需要這些功能,而事實(shí)上,我不認(rèn)為我需要它們,直到我們開始了我們?cè)趦?nèi)部系統(tǒng)進(jìn)行系統(tǒng)測(cè)試。

我們有一些結(jié)點(diǎn),可以通過一定的后臺(tái)任務(wù)基本認(rèn)證。這些被修改,數(shù)據(jù)是從一個(gè)外部存儲(chǔ)中檢索,如S3。注意,這并不總是可能的,所以使用的可能不是你想要的答案。

Oauth2只是我選擇的標(biāo)準(zhǔn)。在理論上,你可以使用facebook授權(quán)來實(shí)現(xiàn)類似的結(jié)果。你也可以將這種方法限速,或存儲(chǔ)在數(shù)據(jù)庫中的不同的訪問級(jí)別如在你的Lua腳本方便操作和檢索使用。如果你真的很無聊,你可以重新實(shí)現(xiàn)基本認(rèn)證在Lua,這只需要你。

有沒有測(cè)試控制系統(tǒng)等。測(cè)試者會(huì)害怕當(dāng)他們意識(shí)到這將是一段時(shí)間的集成測(cè)試。你可以重新運(yùn)行上面的嘲笑為全球范圍內(nèi)注入變量以及執(zhí)行腳本,但它不是理想的設(shè)置。

你還需要修改應(yīng)用程序識(shí)別你的新的訪問標(biāo)頭。內(nèi)部工具將是最簡(jiǎn)單的,但你可能需要為供應(yīng)商軟件作出一定的讓步。

鏈接

一些博客中的講解及研究實(shí)例

鏈接

SeatGeek Homebrew Formulae with customizable nginx

nginx_release.sh for building nginx debs

access.lua and nginx-site

另一些講解閱讀

HttpLuaModule

proxy_pass, like mod_proxy, but for nginx

Lua usage at Mixlr

OAuth for Apache

Homebrew OS X Package Manager


原文:Yak Shaving: Adding OAuth Support to Nginx via Lua
轉(zhuǎn)載自:開源中國 - Garfielt, LeoXu, DrZ, BoydWang, 學(xué)習(xí)者8, FreeZ

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/39051.html

相關(guān)文章

  • CentOS 5.8 上安裝 systemtap-2.6

    摘要:最近為了分析公司的一個(gè)的應(yīng)用性能,正好需要用到春神的那套的分析腳本,因此就立馬去搭建下環(huán)境依賴項(xiàng)依賴于注上面安裝的時(shí)候,的版本不能太高,不然會(huì)報(bào)錯(cuò),安裝失敗,目前使用的版本是該文的前提是你已經(jīng)編譯安裝好了支持的版本,以及你已經(jīng) 最近為了分析公司的一個(gè) nginx + lua 的應(yīng)用性能,正好需要用到春神的那套 nginx-lua 的分析腳本,因此就立馬去搭建下 環(huán)境: CentO...

    leoperfect 評(píng)論0 收藏0
  • CentOS 5.8 上安裝 systemtap-2.6

    摘要:最近為了分析公司的一個(gè)的應(yīng)用性能,正好需要用到春神的那套的分析腳本,因此就立馬去搭建下環(huán)境依賴項(xiàng)依賴于注上面安裝的時(shí)候,的版本不能太高,不然會(huì)報(bào)錯(cuò),安裝失敗,目前使用的版本是該文的前提是你已經(jīng)編譯安裝好了支持的版本,以及你已經(jīng) 最近為了分析公司的一個(gè) nginx + lua 的應(yīng)用性能,正好需要用到春神的那套 nginx-lua 的分析腳本,因此就立馬去搭建下 環(huán)境: CentO...

    fuchenxuan 評(píng)論0 收藏0
  • OpenResty安裝、配置與使用

    摘要:用于方便地搭建能夠處理超高并發(fā)擴(kuò)展性極高的動(dòng)態(tài)應(yīng)用服務(wù)和動(dòng)態(tài)網(wǎng)關(guān)。安裝安裝依賴庫下載及安裝激活組件被用于構(gòu)建。大部組件默認(rèn)是激活的,也有部件不是。您需要通過以下選項(xiàng)在編譯的時(shí)候?qū)⑺鼈兏髯约せ?,和? OpenResty簡(jiǎn)介 OpenResty 是一個(gè)基于 Nginx 與 Lua 的高性能 Web 平臺(tái),其內(nèi)部集成了大量精良的 Lua 庫、第三方模塊以及大多數(shù)的依賴項(xiàng)。用于方便地搭建能夠處...

    stackfing 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<