\1. Nginx來處理存取控制的方法有多種,實現的效果也有多種,存取IP段,存取內容限制,存取頻率限制等。
\2. 用Nginx Lua Redis來做存取限制主要是考慮到高並發環境下快速存取控制的需求。
\3. Nginx處理請求的過程總共分割為11個階段,分別是:
post-read、server-rewrite、find-config、rewrite、post-rewrite、 preaccess、access、post-access、try-files、content、log.
在openresty中,可以找到:
set_by_lua,access_by_lua,content_by_lua,rewrite_by_lua等方法。
那麼存取控制應該是,access階段。
依照正常的邏輯思維,我們會想到的存取控制方案如下:
1.偵測是否被forbidden? =》是,forbidden是否到期:是,清除記錄,返回200,正常訪問;否,返回403; =》否,返回200,正常訪問
2.每次訪問,訪問用戶的訪問頻率1處理
3.檢測訪問頻率是否超過限制,超過即添加forbidden記錄,返回403
這是簡單地方案,還可以添加點枝枝葉葉,訪問禁止時間通過演算法導入,每次凹曲線增加。
首先為nginx新增vhost設定文件,vhost.conf部分內容如下:
lua_package_path "/usr/local/openresty/lualib/?.lua;;";#告诉openresty库地址lua_package_cpath "/usr/local/openresty/lualib/?.so;;"; error_log /usr/local/openresty/nginx/logs/openresty.debug.log debug; server { listen 8080 default; server_name www.ttlsa.com; root /www/openresty; location /login { default_type 'text/html'; access_by_lua_file "/usr/local/openresty/nginx/lua/access_by_redis.lua";#通过lua来处理访问控制 } }
Access_by_redis.lua
#經過參考v2ex.com的實現,我們發現使用簡單的字串儲存方案就已經足夠,因此選擇了redis作為儲存方式。 key分別是:
使用者登入記錄:user:127.0.0.1:time(unix時間戳記)
存取限制:block:127.0.0.1
先連接Redis吧:
local red = redis:new()function M:redis() red:set_timeout(1000)local ok, err = red:connect("127.0.0.1", 6379)if not ok then ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) end end
按照我們的邏輯方案,第二步是,檢測是否forbidden,下面我們就檢測block:127.0.0.1,如果搜尋到數據,檢測時間是否過期,未過期返回403,否則直接回傳200:
function M:check1()local time=os.time() --system timelocal res, err = red:get("block:"..ngx.var.remote_addr)if not res then -- redis error ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) --redis get data error endif type(res) == "string" then --if red not null then type(red)==string if tonumber(res) >= tonumber(time) then --check if forbidden expired ngx.exit(ngx.HTTP_FORBIDDEN) --ngx.say("forbidden") end end }
接下來會做偵測,是否存取頻率過高,如果過高,要拉到黑名單的,
實作的方法是,偵測user:127.0.0.1 :time的值是否超標:
function M:check2()local time=os.time() --system timelocal res, err = red:get("user:"..ngx.var.remote_addr..":"..time)if not res then -- redis error ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) --redis get data error endif type(res) == "string" then if tonumber(res) >= 10 then -- attack, 10 times request/s red:del("block:"..self.ip) red:set("block:"..self.ip, tonumber(time)+5*60 ) --set block time ngx.exit(ngx.HTTP_FORBIDDEN) end end end
最後呢,還要記得,把每次訪問時間做一個自增長,user:127.0.0.1:time:
function M:add()local time=os.time() --system time ok, err = red:incr("user:"..ngx.var.remote_addr..":"..time)if not ok then ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) --redis get data error end end
那麼,測試,強刷幾次瀏覽器,發現過一會兒,返回了403,ok,搞定。
以上是怎麼使用lua進行nginx redis存取控制的詳細內容。更多資訊請關注PHP中文網其他相關文章!