基于时间窗口的访问控制是一种限制客户端在特定时间段内访问频率的方法,常用于防止暴力破解、DDoS攻击或API滥用。在Nginx反向代理环境中,可以通过多种方式实现这种控制。
http {
limit_req_zone $binary_remote_addr zone=timewindow:10m rate=10r/m;
server {
location / {
limit_req zone=timewindow burst=20 nodelay;
proxy_pass http://backend;
}
}
}
参数说明:
- zone=timewindow:10m
: 创建10MB大小的共享内存区域
- rate=10r/m
: 每分钟允许10个请求
- burst=20
: 允许突发20个请求
- nodelay
: 立即处理突发请求而不延迟
需要安装OpenResty或ngx_http_lua_module模块:
http {
lua_shared_dict time_window 10m;
server {
location / {
access_by_lua_block {
local limit = require "resty.limit.req"
local lim, err = limit.new("time_window", 10, 60) -- 每分钟10个请求
if not lim then
ngx.log(ngx.ERR, "failed to instantiate a resty.limit.req object: ", err)
return ngx.exit(500)
end
local key = ngx.var.binary_remote_addr
local delay, err = lim:incoming(key, true)
if not delay then
if err == "rejected" then
return ngx.exit(503)
end
ngx.log(ngx.ERR, "failed to limit req: ", err)
return ngx.exit(500)
end
}
proxy_pass http://backend;
}
}
}
限制特定时间段内的访问:
http {
map $time_iso8601 $allowed {
default 1;
"~^2023-\d{2}-\d{2}T(09|10|11|12|13|14|15|16|17):" 0; # 9:00-17:59禁止访问
}
server {
location / {
if ($allowed = 0) {
return 403;
}
proxy_pass http://backend;
}
}
}
使用Redis+Lua实现精确的滑动时间窗口控制:
http {
lua_shared_dict redis_connections 1m;
server {
location / {
access_by_lua_block {
local redis = require "resty.redis"
local red = redis:new()
red:set_timeout(1000) -- 1秒超时
local ok, err = red:connect("redis-host", 6379)
if not ok then
ngx.log(ngx.ERR, "failed to connect to Redis: ", err)
return ngx.exit(500)
end
local key = "rate_limit:" .. ngx.var.binary_remote_addr
local limit = 100 -- 100请求
local window = 60 -- 60秒窗口
local current = red:get(key)
if current == ngx.null then
red:setex(key, window, 1)
elseif tonumber(current) < limit then
red:incr(key)
else
red:close()
return ngx.exit(429)
end
red:close()
}
proxy_pass http://backend;
}
}
}
limit_req_zone
的内存区域大小需要根据预期IP数量设置以上方案可根据实际业务需求和安全要求进行组合和调整。