location / { # 如果该location 下存在静态资源文件可以做一个判断 #if ($request_uri ~ .*\.(html|htm|jpg|js|css)) { # access_by_lua_file /usr/local/lua/access_limit.lua; #} access_by_lua_file /usr/local/lua/access_limit.lua; # 加上了这条配置,则会根据 access_limit.lua 的规则进行限流 alias /usr/local/web/; index index.html index.htm; }配置 Lua 脚本
/usr/local/lua/access_limit.lua -- 可以实现自动将访问频次过高的IP地址加入黑名单封禁一段时间 --连接池超时回收毫秒 local pool_max_idle_time = 10000 --连接池大小 local pool_size = 100 --redis 连接超时时间 local redis_connection_timeout = 100 --redis host local redis_host = "your redis host ip" --redis port local redis_port = "your redis port" --redis auth local redis_auth = "your redis authpassword"; --封禁IP时间(秒) local ip_block_time= 120 --指定ip访问频率时间段(秒) local ip_time_out = 1 --指定ip访问频率计数最大值(次) local ip_max_count = 3 -- 错误日志记录 localfunction errlog(msg, ex) ngx.log(ngx.ERR, msg, ex) end -- 释放连接池 localfunction close_redis(red) if not red then return end local ok, err = red:set_keepalive(pool_max_idle_time, pool_size) if not ok then ngx.say("redis connct err:",err) return red:close() end end --连接redis local redis = require "resty.redis" local client = redis:new() local ok, err = client:connect(redis_host, redis_port) -- 连接失败返回服务器错误 if not ok then close_redis(client) ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) end --设置超时时间 client:set_timeout(redis_connection_timeout) -- 优化验证密码操作 代表连接在连接池使用的次数,如果为0代表未使用,不为0代表复用 在只有为0时才进行密码校验 local connCount, err = client:get_reused_times() -- 新建连接,需要认证密码 if 0 == connCount then local ok, err = client:auth(redis_auth) if not ok then errlog("failed to auth: ", err) return end --从连接池中获取连接,无需再次认证密码 elseif err then errlog("failed to get reused times: ", err) return end -- 堆代码 duidaima.com -- 获取请求ip localfunction getIp() local clientIP = ngx.req.get_headers()["X-Real-IP"] if clientIP == nil then clientIP = ngx.req.get_headers()["x_forwarded_for"] end if clientIP == nil then clientIP = ngx.var.remote_addr end return clientIP end local cliendIp = getIp(); local incrKey = "limit:count:"..cliendIp local blockKey = "limit:block:"..cliendIp --查询ip是否被禁止访问,如果存在则返回403错误代码 local is_block,err = client:get(blockKey) if tonumber(is_block) == 1 then ngx.exit(ngx.HTTP_FORBIDDEN) close_redis(client) end local ip_count, err = client:incr(incrKey) if tonumber(ip_count) == 1 then client:expire(incrKey,ip_time_out) end --如果超过单位时间限制的访问次数,则添加限制访问标识,限制时间为ip_block_time if tonumber(ip_count) > tonumber(ip_max_count) then client:set(blockKey,1) client:expire(blockKey,ip_block_time) end close_redis(client)总结
限制访问频率: 黑名单可以限制某个IP在特定时间段内的访问次数,防止恶意用户进行暴力破解、刷票等行为。