OpenResty使用Lua大全(十)实战: Lua + Redis 实现动态封禁 IP

@[TOC]

系列文章索引

OpenResty使用Lua大全(一)Lua语法入门实战 OpenResty使用Lua大全(二)在OpenResty中使用Lua OpenResty使用Lua大全(三)OpenResty使用Json模块解析json OpenResty使用Lua大全(四)OpenResty中使用Redis OpenResty使用Lua大全(五)OpenResty中使用MySQL OpenResty使用Lua大全(六)OpenResty发送http请求 OpenResty使用Lua大全(七)OpenResty使用全局缓存 OpenResty使用Lua大全(八)OpenResty执行流程与阶段详解 OpenResty使用Lua大全(九)实战:nginx-lua-redis实现访问频率控制 OpenResty使用Lua大全(十)实战: Lua + Redis 实现动态封禁 IP OpenResty使用Lua大全(十一)实战: nginx实现接口签名安全认证 OpenResty使用Lua大全(十二)实战: 动手实现一个网关框架

一、需求背景

为了封禁某些爬虫或者恶意用户对服务器的请求,我们需要建立一个动态的 IP 黑名单。 对于黑名单之内的 IP ,拒绝提供服务。

二、设计方案

1、实现目标

实现 IP 黑名单的功能有很多途径: 1、在操作系统层面,配置 iptables,拒绝指定 IP 的网络请求; 2、在 Web Server 层面,通过 Nginx 自身的 deny 选项 或者 lua 插件 配置 IP 黑名单; 3、在应用层面,在请求服务之前检查一遍客户端 IP 是否在黑名单。

为了方便管理和共享,我们通过 Nginx+Lua+Redis 的架构实现 IP 黑名单的功能

2、nginx.conf配置

在http部分,配置本地缓存,来缓存redis中的数据,避免每次都请求redis

lua 复制代码
lua_shared_dict shared_ip_blacklist 1m; #定义ip_blacklist 本地缓存变量

location /ipblacklist {
	access_by_lua_file /usr/local/openresty/nginx/conf/access_by_limit_ip.lua;
	echo "ipblacklist";
}

3、access_by_limit_ip.lua

lua 复制代码
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_time, pool_size)  
    if not ok then  
        ngx.say("set keepalive error : ", err)  
    end  
end

local function errlog(...)
    ngx.log(ngx.ERR, "redis: ", ...)
end

local function duglog(...)
    ngx.log(ngx.DEBUG, "redis: ", ...)
end

local function getIp()
	local myIP = ngx.req.get_headers()["X-Real-IP"]
	if myIP == nil then
		myIP = ngx.req.get_headers()["x_forwarded_for"]
	end
	if myIP == nil then
		myIP = ngx.var.remote_addr
	end
	return myIP;
end

local key = "limit:ip:blacklist"
local ip = getIp();
local shared_ip_blacklist = ngx.shared.shared_ip_blacklist

--获得本地缓存的最新刷新时间
local last_update_time = shared_ip_blacklist:get("last_update_time");

if last_update_time ~= nil then 
	local dif_time = ngx.now() - last_update_time 
	if dif_time < 60 then --缓存1分钟,没有过期
		if shared_ip_blacklist:get(ip) then
			return ngx.exit(ngx.HTTP_FORBIDDEN) --直接返回403
		end
		return
	end
end

local redis = require "resty.redis"  --引入redis模块
local red = redis:new()  --创建一个对象,注意是用冒号调用的

--设置超时(毫秒)  
red:set_timeout(1000) 
--建立连接  
local ip = "127.0.0.1"  
local port = 6379
local ok, err = red:connect(ip, port)
if not ok then  
	close_redis(red)
	errlog("limit ip cannot connect redis");
else
	local ip_blacklist, err = red:smembers(key);
	
	if err then
		errlog("limit ip smembers");
	else
		--刷新本地缓存,重新设置
		shared_ip_blacklist:flush_all();
		
		--同步redis黑名单 到 本地缓存
		for i,bip in ipairs(ip_blacklist) do
			--本地缓存redis中的黑名单
			shared_ip_blacklist:set(bip,true);
		end
		--设置本地缓存的最新更新时间
		shared_ip_blacklist:set("last_update_time",ngx.now());
	end
end  

if shared_ip_blacklist:get(ip) then
	return ngx.exit(ngx.HTTP_FORBIDDEN) --直接返回403
end

4、测试

用户redis客户端设置 sadd limit:ip:blacklist 192.168.56.1,设置黑名单访问测试。

相关推荐
csucoderlee1 小时前
Go语言中结构体字面量
开发语言·后端·golang
zimoyin7 小时前
Kotlin 使用 Springboot 反射执行方法并自动传参
spring boot·后端·kotlin
SomeB1oody9 小时前
【Rust自学】18.1. 能用到模式(匹配)的地方
开发语言·后端·rust
LiuYuHani9 小时前
Spring Boot面试题
java·spring boot·后端
萧月霖9 小时前
Scala语言的安全开发
开发语言·后端·golang
电脑玩家粉色男孩9 小时前
八、Spring Boot 日志详解
java·spring boot·后端
ChinaRainbowSea10 小时前
八. Spring Boot2 整合连接 Redis(超详细剖析)
java·数据库·spring boot·redis·后端·nosql
叫我DPT11 小时前
Go 中 defer 的机制
开发语言·后端·golang
我们的五年11 小时前
【Linux网络编程】:守护进程,前台进程,后台进程
linux·服务器·后端·ubuntu
谢大旭12 小时前
ASP.NET Core自定义 MIME 类型配置
后端·c#