108人参与 • 2026-09-30 • Redis
为了封禁某些爬虫或者恶意用户对服务器的请求,我们需要建立一个动态的ip黑名单。对于黑名单中的ip,将拒绝提供服务,并且可以设置封禁失效时间。
| 实现方式 | 优点 | 缺点 |
|---|---|---|
| iptables | 简单直接,物理层拦截 | 需要手动操作,不灵活 |
| nginx+lua+redis | 动态封禁,分布式共享,自动失效 | 需学习lua脚本 |
| 应用层代码 | 实现简单,易于维护 | 代码臃肿,高并发影响性能 |
选型结论:采用 nginx + lua + redis 架构实现ip黑名单功能
客户端请求 → nginx(lua脚本) → redis(检查黑名单/计数) → 后端服务
↓
封禁ip返回403在需要进行限制的server的location中添加配置:
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; # 根据规则进行限流
alias /usr/local/web/;
index index.html index.htm;
}路径:/usr/local/lua/access_limit.lua
-- 自动将访问频次过高的ip地址加入黑名单封禁一段时间
-- ================= 配置参数 =================
-- 连接池超时回收(毫秒)
local pool_max_idle_time = 10000
-- 连接池大小
local pool_size = 100
-- redis连接超时时间(毫秒)
local redis_connection_timeout = 100
-- redis主机
local redis_host = "your_redis_host_ip"
-- redis端口
local redis_port = "6379"
-- redis认证密码
local redis_auth = "your_redis_password"
-- 封禁ip时间(秒)
local ip_block_time = 120
-- 指定ip访问频率时间段(秒)
local ip_time_out = 1
-- 指定ip访问频率计数最大值(次)
local ip_max_count = 3
-- ================= 工具函数 =================
-- 错误日志记录
local function errlog(msg, ex)
ngx.log(ngx.err, msg, ex)
end
-- 释放连接池
local function 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
-- 获取客户端真实ip
local function 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
-- ================= 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
-- ================= 业务逻辑 =================
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
close_redis(client)
ngx.exit(ngx.http_forbidden)
end
-- 增加访问计数
local ip_count, err = client:incr(incrkey)
if tonumber(ip_count) == 1 then
client:expire(incrkey, ip_time_out)
end
-- 如果超过单位时间限制的访问次数,则添加限制访问标识
if tonumber(ip_count) > tonumber(ip_max_count) then
client:set(blockkey, 1)
client:expire(blockkey, ip_block_time)
end
-- 释放redis连接
close_redis(client)
| key格式 | 用途 | 过期时间 |
|---|---|---|
| limit:count:ip地址 | 记录ip在时间段内的访问次数 | ip_time_out(1秒) |
| limit:block:ip地址 | 封禁标识,值为1表示封禁 | ip_block_time(120秒) |
开始
↓
获取客户端ip
↓
连接redis
↓
检查blockkey是否存在?
├─ 是 → 返回403禁止访问
└─ 否 → 继续处理
↓
对incrkey执行incr操作
↓
如果是第一次访问,设置过期时间
↓
检查访问次数是否超过阈值?
├─ 是 → 设置blockkey封禁标识
└─ 否 → 正常访问
↓
释放redis连接
↓
结束
-- 在检查黑名单之前添加白名单判断
local white_list = {"127.0.0.1", "192.168.1.*"}
local function iswhitelist(ip)
for _, value in ipairs(white_list) do
if value == ip or ngx.re.match(ip, value:gsub("%*", ".*")) then
return true
end
end
return false
end
if iswhitelist(cliendip) then
close_redis(client)
return -- 白名单直接放行
end
对于频繁访问的ip,可以重定向到验证码页面:
if tonumber(ip_count) > tonumber(ip_max_count) then
if tonumber(ip_count) < tonumber(ip_max_count) * 2 then
-- 第一次超限,要求验证码
ngx.redirect("/captcha.html")
else
-- 多次超限,直接封禁
client:set(blockkey, 1)
client:expire(blockkey, ip_block_time)
end
end
结合访问日志分析,可实现更智能的封禁策略:
到此这篇关于nginx+lua+redis实现动态封禁ip的几种方法的文章就介绍到这了,更多相关nginx动态封禁ip内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
您想发表意见!!点此发布评论
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
发表评论