使用Lua编写Nginx服务器的认证模块的方法
过去两天里,我解决了一个非常有趣的问题。我用一个nginx服务器作为代理,需要能够向其中添加一个认证层,使其能够使用外部的认证源(比如某个web应用)来进行验证,如果用户在外部认证源有账号,就可以在代理里认证通过。
需求一览
我考虑了几种解决方案,罗列如下:
- 用一个简单的Python/Flask模块来做代理和验证。
- 一个使用subrequests做验证的nginx模块(nginx目前可以做到这一点)
- 使用Lua编写一个nginxren认证模块
很显然,给整个系统添加额外请求将执行的不是很好,因为这将会增加延迟(特别是给每一个页面文件都增加一个请求是很让人烦恼的).这就意味着我们把subrequest模块排除在外了。Python/Flash解决方案好像对nginx支持的也并不好,所以咱也把它排除了。就剩Lua了,当然nginx对原生化支持得不错的。
因为我不想再扩展的服务器上对每一个请求都做认证,所以我决定生成一些令牌,这样人们就可以将它保存起来,并把它呈现给服务器,然后服务器就让请求通过。然而,因为Lua模块没有一种保持状态的方式(我已经发现),所以我们不能将令牌随处存储。当你没有更多的内存时,怎样来验证用户所说的话呢?
解决问题
加密签名的方式可是咱的救星!我们可以拿用户的用户名和过期时间数据来给用户添加签名的cookies,这样就能很容易的验证每个用户是谁了,同时我们就不用令牌了。
在nginx中,我们要做的就是直接在指定位置配置access_by_lua_file/our/file.lua,这样这个指定位置就可以保护我们的脚本了。现在,让我们一起来写代码:
--Somevariabledeclarations. localcookie=ngx.var.cookie_MyToken localhmac="" localtimestamp="" localtimestamp_time=0
--Checkthatthecookieexists. ifcookie==nilorcookie:find(":")==nilthen --InternallyrewritetheURLsothatweserve --/auth/ifthere'snocookie. ngx.exec("/auth/") else --Ifthere'sacookie,splitofftheHMACsignature --andtimestamp. localdivider=cookie:find(":") hmac=cookie:sub(divider+1) timestamp=cookie:sub(0,divider-1) end
--Verifythatthesignatureisvalid. ifhmac_sha1("someverysecretstring",timestamp)~=hmacortonumber(timestamp)<os.time()then --Ifinvalid,sendto/auth/again. ngx.exec("/auth/") end