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";


  测试效果如下:
posted @   锐洋智能  阅读(340)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Obsidian + DeepSeek:免费 AI 助力你的知识管理,让你的笔记飞起来!
· 分享4款.NET开源、免费、实用的商城系统
· 解决跨域问题的这6种方案,真香!
· 5. Nginx 负载均衡配置案例(附有详细截图说明++)
· Windows 提权-UAC 绕过
点击右上角即可分享
微信分享提示