nginx+lua+redis实现验证码防采集,nginxredis


之前介绍过在nginx里如何嵌入lua模块,利用nginx+lua可以很好的开发开发nginx的业务逻辑,并且达到高并发的效果。

下面我们就来介绍下利用nginx+lua+redis实现防采集的功能。

现象:

网站在为用户提供服务的同时也在被搜索引擎、采集器不断的抓取,可能会造成网站不堪重负,导致页面放回5XX错误。针对此种情况,我们就要对采集器及搜索引擎来进行访问控制,当然对搜索引擎的控制可能会影响网站的收录。

功能描述:

nginx+lua在前端实现客户端的访问控制,将客户端的访问信息记入redis中,如果超过访问频率的限制,则跳转到php生成的验证码界面;若验证通过则可以继续访问半小时,若验证不通过则被封锁半小时。由于采集器的ip可能变化,所以在此不会一直封锁。

1.nginx_lua模块安装

请参考前面“nginx和lua”的博文http://blog.csdn.net/yanggd1987/article/details/46679989

2.lua-resty-redis模块安装

cd /usr/local/src
wget https://github.com/openresty/lua-resty-redis/archive/master.zip
unzip master.zip
cd lua-resty-redis-master
mkdir -p /usr/local/nginx/lua
#将lib拷贝到nginx安装目录下的lua文件夹内
cp -rf lib /usr/local/nginx/lua
cd /usr/local/nginx/lua/lib
ln -s redis.lua resty/redis.lua
3.在nginx目录下编写lua脚本
cd /usr/local/nginx/lua
vim access_test.lua
package.path = "/usr/local/nginx/lua/?.lua;/usr/local/nginx/lua/lib/?.lua;"
package.cpath = "/usr/local/nginx/lua/?.so;/usr/local/nginx/lua/lib/?.so;"
--封禁ip时间
ip_bind_time = 300
--ip访问频率时间段
ip_time_out = 60
--ip访问频率计数最大值
connect_count = 60
--连接redis
local redis = require "resty.redis"
local cache = redis.new()
local ok ,err = cache.connect(cache,"10.10.10.8","6381")
cache:set_timeout(60000)

--如果连接失败,跳转到label处
if not ok then
        goto label
end

--ip封禁key
is_bind , err = cache:get("bind_"..ngx.var.remote_addr)

--白名单
--验证码通过后,只需将white_ngx.var.remote_addr置为1并设置过期时间即可,在下次访问时将不会再做判断
is_white , err = cache:get("white_"..ngx.var.remote_addr)

if tonumber(is_white) == 1 then
        goto label
end


--查询ip是否在封禁时间段内,若在则跳转到验证码页面
if tonumber(is_bind) == 1 then
        --ngx.say("block,跳转到验证码页")
        --base64编码
        local source=ngx.encode_base64(ngx.var.scheme.."://"..ngx.var.host..ngx.var.request_uri)
        local dest="http://10.10.10.8/authcode.html".."?continue="..source
        --url_args编码
        --local source=ngx.encode_args({continue=ngx.var.scheme.."://"..ngx.var.host..ngx.var.request_uri})
        --local dest="http://10.10.10.8/authcode.html".."?"..source
        ngx.redirect(dest,302)
        goto label
end

--ip记录时间key
start_time , err = cache:get("time_"..ngx.var.remote_addr)
--ip计数key
ip_count , err = cache:get("count_"..ngx.var.remote_addr)

--如果ip记录时间的key不存在或者当前时间减去ip记录时间大于指定时间间隔,则重置时间key和计数key
--如果当前时间减去ip记录时间小于指定时间间隔,则ip计数+1,并且ip计数大于指定ip访问频率,则设置ip的封禁key为1,同时设置封禁key的过期时间为封禁ip时间
if start_time == ngx.null or os.time() - tonumber(start_time) > ip_time_out then
        res , err = cache:set("time_"..ngx.var.remote_addr , os.time())
        res , err = cache:set("count_"..ngx.var.remote_addr , 1)
else
        ip_count = ip_count + 1
        res , err = cache:incr("count_"..ngx.var.remote_addr)
        if ip_count >= connect_count then
                res , err = cache:set("bind_"..ngx.var.remote_addr , 1)
                --以下步骤交给php,若验证码不通过则设置bind过期时间,若验证码通过则设置white_ip为1并且设置其过期时间
                --res , err = cache:expire("bind_"..ngx.var.remote_addr , ip_bind_time)
        end
end


::label::
local ok , err = cache:close()

10.10.10.8/authcode.html为验证码页面,需要其他语言编写,在此就不再说了!

注意:

1.在生产环境中由于后端php需要记录远程客户端ip,因此需要在nginx代理上开启相关设置:

proxy_set_header   X-Real-IP        $remote_addr;
proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;

2.在应用生产环境中可能是多域名环境,ngx.exec和ngx.redirect的跳转方式不同,ngx.exec为内部跳转,ngx.redirect为外部跳转;

3.white_ip为白名单,若验证成功则添加白名单,并且设置白名单的过期时间;若验证不成功,则直接设置bind_ip的过期时间;若不验证,则会一直被封锁;

4.判断white_ip一定要放在bind_ip上面,因为验证已经添加白名单后则会直接跳过后续判断部分;

5.在跳转到验证码页面后,需要记录所要访问页面的url,以达到验证通过后跳转到所要访问的页面;

6.在访问时间内没有达到限制次数,则cout_ip和time_ip会被重置。

4.将lua脚本添加到相应的location下

   location /test {
                access_by_lua_file '/usr/local/nginx1.6/lua/access.lua';
                content_by_lua 'ngx.header.content_type = "text/plain"
                ngx.say("hello,world")
                ';
        }

在1分钟内,当访问次数达到100,就会跳转到验证码界面。





ps:在此感谢zengbin3013(http://blog.csdn.net/zengbin3013/article/details/9313979)博主,本脚本也是在他的基础上改的。

参考内容:

https://github.com/openresty/lua-resty-redis

https://github.com/openresty/lua-nginx-module

http://wiki.nginx.org/HttpLuaModule




相关内容

    暂无相关文章