Предисловие: теперь производственная система часто сканируется поисковыми роботами, что серьезно повлияло на использование обычными пользователями, поэтому необходимо найти способы уменьшить количество таких вредоносных запросов. Моей первой мыслью было использовать параметры, которые поставляются с Nginx для ограничения, но после тестирования выяснилось, что это непросто. Поэтому я окончательно решил использовать Lua+Redis для ограничения частоты доступа IP, поэтому у меня есть эта статья
Техническая подготовка
(1) Программирование Lua в Nginx:
Для этого я решил установить OpenResty. OpenResty, также известный как «ngx_openresty», представляет собой сервер веб-приложений с Nginx в качестве ядра и множеством сторонних модулей. Для установки и использования OpenResty под Linux обратитесь к моей предыдущей статье:www.zifangsky.cn/1020.html
Кроме того, для базового использования программирования Lua в Nginx вы можете обратиться к моей предыдущей статье:www.zifangsky.cn/1024.html
(2) Количество посещений одного IP-адреса в единицу времени сохраняется:
Поскольку объем сохраняемых данных невелик, а время сохранения очень короткое (автоматическое истечение 30 секунд), я выбрал Redis, который сейчас используется чаще всего. Для установки одного узла Redis обратитесь к моей предыдущей статье:www.zifangsky.cn/823.html
Реализация двух кодов и тестирование
(1) Lua-скрипт для добавления контроля доступа:
[root@hbase31 ~]# vim /usr/local/openresty/nginx/conf/lua/access.lua
Его содержание следующее:
local ip_block_time=300 --封禁IP时间(秒)
local ip_time_out=30 --指定ip访问频率时间段(秒)
local ip_max_count=20 --指定ip访问频率计数最大值(秒)
local BUSINESS = ngx.var.business --nginx的location中定义的业务标识符
--连接redis
local redis = require "resty.redis"
local conn = redis:new()
ok, err = conn:connect("192.168.1.30", 6379)
conn:set_timeout(2000) --超时时间2秒
--如果连接失败,跳转到脚本结尾
if not ok then
goto FLAG
end
--查询ip是否被禁止访问,如果存在则返回403错误代码
is_block, err = conn:get(BUSINESS.."-BLOCK-"..ngx.var.remote_addr)
if is_block == '1' then
ngx.exit(403)
goto FLAG
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
ip_count = ip_count + 1 --存在则将单位时间内的访问次数加1
if 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:set(BUSINESS.."-COUNT-"..ngx.var.remote_addr,ip_count)
res, err = conn:expire(BUSINESS.."-COUNT-"..ngx.var.remote_addr, ip_time_out)
end
end
-- 结束标记
::FLAG::
local ok, err = conn:close()
Цель этого скрипта проста: если IP-адрес обращается 20 раз в течение 30 секунд, это означает, что частота доступа к IP-адресу слишком высока, поэтому IP-адрес блокируется на 5 минут. В то же время, поскольку время тайм-аута подсчитываемого KEY в Redis установлено на 30 секунд, отсчет будет перезапущен, если интервал между двумя обращениями будет больше 30 секунд.
(2) Обратитесь к приведенному выше сценарию в том месте, где Nginx необходимо ограничить скорость:
location /user/ {
set $business "USER";
access_by_lua_file /usr/local/openresty/nginx/conf/lua/access.lua;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://user_224/user/;
}
Примечание: Для front-end страниц с большим количеством статических файлов ресурсов (таких как: js, css, картинки и т.д.) можно установить ограничение скорости доступа только для запросов в указанном формате.Пример кода следующий :
location /h5 {
if ($request_uri ~ .*\.(html|htm|jsp|json)) {
set $business "H5";
access_by_lua_file /usr/local/openresty/nginx/conf/lua/access.lua;
}
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://h5_224/h5;
}
(3) тест:
Зайдите в браузере: http://192.168.1.31:3000/user/services, вы можете обнаружить, что можете нормально просматривать:
На этом этапе войдите в Redis, и вы увидите, что отсчет начался:
Затем используйте F5, чтобы повторно обновить страницу http://192.168.1.31:3000/user/services, вы можете обнаружить, что браузер отображает страницу 403 через несколько раз, а затем к ней нельзя будет получить доступ снова, пока не пройдет 5 минут.
Ссылаться на: