Lua 脚本限制访问频率过高的IP
nginx +redis + lua
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | local function close_redis(red) if not red then return end local pool_max_idle_time = 10000 local pool_size = 100 local ok, err = red:set_keepalive(pool_max_idle_tme, pool_size) if not ok then ngx.say( "set keepalive err : " , err) end end local ip_block_time = 120 - - 封禁IP时间(秒) local ip_time_out = 30 - - 指定ip访问频率时间段(秒) local ip_max_count = 40 - - 指定ip访问频率计数最大值(秒) local BUSINESS = "sqd-ratelimit" - - nginx的location中定义的业务标识符,我这里写死了。 - - 连接redis local redis = require "resty.redis" local conn = redis:new() ok, err = conn:connect( "127.0.0.1" , 6379 ) conn:set_timeout( 2000 ) - - 超时时间 2 秒 - - 如果连接失败,跳转到脚本结尾 if not ok then - - goto FLAG close_redis(conn) end local count, err = conn:get_reused_times() if 0 = = count then - - - - 新建连接,需要认证密码 ok, err = conn:auth( "yourredispassword" ) if not ok then ngx.say( "failed to auth: " , err) return end elseif err then - - - - 从连接池中获取连接,无需再次认证密码 ngx.say( "failed to get reused times: " , err) return end - - 查询ip是否被禁止访问,如果存在则返回 403 错误代码 is_block, err = conn:get(BUSINESS .. "-BLOCK-" .. ngx.var.remote_addr) if is_block = = '1' then ngx.exit( 429 ) close_redis(conn) end - - 查询redis中保存的ip的计数器 ip_count, err = conn:get(BUSINESS .. "-COUNT-" .. ngx.var.remote_addr) if ip_count = = ngx.null then - - 如果不存在,则将该IP存入redis,并将计数器设置为 1 、该KEY的超时时间为ip_time_out res, err = conn: set (BUSINESS .. "-COUNT-" .. ngx.var.remote_addr, 1 ) res, err = conn:expire(BUSINESS .. "-COUNT-" .. ngx.var.remote_addr, ip_time_out) else if tonumber(ip_count) > = ip_max_count then - - 如果超过单位时间限制的访问次数,则添加限制访问标识,限制时间为ip_block_time res, err = conn: set (BUSINESS .. "-BLOCK-" .. ngx.var.remote_addr, 1 ) res, err = conn:expire(BUSINESS .. "-BLOCK-" .. ngx.var.remote_addr, ip_block_time) else res, err = conn:incr(BUSINESS .. "-COUNT-" .. ngx.var.remote_addr) end end - - 结束标记 local ok, err = conn:close() |
部署
在对应的location中添加下面的代码
access_by_lua_file "/usr/local/nginx/conf/reyo/rate-limit.lua";
测试效果如下:

· Obsidian + DeepSeek:免费 AI 助力你的知识管理,让你的笔记飞起来!
· 分享4款.NET开源、免费、实用的商城系统
· 解决跨域问题的这6种方案,真香!
· 5. Nginx 负载均衡配置案例(附有详细截图说明++)
· Windows 提权-UAC 绕过