给 Nginx 增加 OAuth 支持(nginx(3)

此时,我们需要从我们的api接口获取一个TOKEN。nginx-lua提供了ngx.location.capture方法,支持发起一个内部请求到redis,并接收响应。这意味着,我们不能直接调用类似于,但我们可以用proxy_pass把这种外部链接包装成内部请求。

我们通常约定给这样的内部请求前面加一个_(下划线), 用来阻止外部直接访问。

-- 第一步,从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) -- 终止所有非法请求 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 请求失败 if not access_token then -- 跟踪用户访问,用于透明的重定向 ngx.header["Set-Cookie"] = "SGRedirectBack="..nginx_uri.."; path=/;Max-Age=120" -- 重定向到 /oauth , 获取权限 return ngx.redirect("internal-oauth:1337/oauth?client_id="..app_id.."&scope=all") end end

此时在Lua脚本中,应该已经有了一个可用的access_token。我们可以用来获取任何请求需要的用户信息。在本文中,返回401表示没有权限,403表示token过期,并且授权信息用简单数字打包成json响应。

-- 确保用户有访问web应用的权限 -- 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 -- 没有权限 ngx.status = res.status ngx.say("{"status": 503, "message": "Error accessing api/me for credentials"}") return ngx.exit(ngx.HTTP_OK) end

现在,我们已经验证了用户确实是经过身份验证的并且具有某个级别的访问权限,我们可以检查他们的访问级别,看看是否同我们所定义的任何当前端点的访问级别有冲突。我个人在这一步删除了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

现在我们只需要通过一些请求头信息告知我们当前的应用谁登录了就行了。您可以重用REMOTE_USER,如果你有需求的话,就可以用这个取代基本的身份验证,而除此之外的任何事情都是公平的游戏。

-- 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)

我现在就可以像任何其它的站点那样在我的应用程序中访问这些http头了,而不是用数百行代码和大量的时间来重新实现身份验证。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/cd2e60c00ed9a5af75a6fea44bd2201d.html