广告位联系
返回顶部

Nginx+Lua+Redis实现动态封禁IP的几种方法

nginx 来源:互联网 作者:佚名 发布时间:2026-09-30 19:40:55 人浏览
摘要

一、需求背景 为了封禁某些爬虫或者恶意用户对服务器的请求,我们需要建立一个动态的IP黑名单。对于黑名单中的IP,将拒绝提供服务,并且可以设置封禁失效时间。 二、环境准备 Linux版本

一、需求背景

为了封禁某些爬虫或者恶意用户对服务器的请求,我们需要建立一个动态的IP黑名单。对于黑名单中的IP,将拒绝提供服务,并且可以设置封禁失效时间。

二、环境准备

  • Linux版本:CentOS 7 / Ubuntu等
  • Redis版本:5.0.5+
  • Nginx版本:OpenResty(内置Lua支持)

三、设计方案对比

实现方式 优点 缺点
iptables 简单直接,物理层拦截 需要手动操作,不灵活
Nginx+Lua+Redis 动态封禁,分布式共享,自动失效 需学习Lua脚本
应用层代码 实现简单,易于维护 代码臃肿,高并发影响性能

选型结论:采用 Nginx + Lua + Redis 架构实现IP黑名单功能

架构图

1

2

3

客户端请求 → Nginx(Lua脚本) → Redis(检查黑名单/计数) → 后端服务

                     ↓

              封禁IP返回403

四、配置Nginx

1. 修改nginx.conf

在需要进行限制的server的location中添加配置:

1

2

3

4

5

6

7

8

9

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;

}

五、Lua脚本实现

1. 创建脚本文件

路径:/usr/local/lua/access_limit.lua

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

-- 自动将访问频次过高的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)

六、Redis数据结构说明

Key格式 用途 过期时间
limit:count:IP地址 记录IP在时间段内的访问次数 ip_time_out(1秒)
limit:block:IP地址 封禁标识,值为1表示封禁 ip_block_time(120秒)

七、工作流程图

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

开始

  ↓

获取客户端IP

  ↓

连接Redis

  ↓

检查blockKey是否存在?

  ├─ 是 → 返回403禁止访问

  └─ 否 → 继续处理

        ↓

  对incrKey执行incr操作

        ↓

  如果是第一次访问,设置过期时间

        ↓

  检查访问次数是否超过阈值?

        ├─ 是 → 设置blockKey封禁标识

        └─ 否 → 正常访问

              ↓

        释放Redis连接

              ↓

          结束

八、高级功能扩展

1. 白名单机制

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

-- 在检查黑名单之前添加白名单判断

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

2. 验证码验证

对于频繁访问的IP,可以重定向到验证码页面:

1

2

3

4

5

6

7

8

9

10

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

3. 异常检测自动封禁

结合访问日志分析,可实现更智能的封禁策略:

  • 短时间内404错误过多
  • 请求路径异常(扫描行为)
  • User-Agent特征匹配

九、总结

方案优势

  1. 配置简单轻量:对服务器性能影响小
  2. 分布式共享:多台服务器通过共享Redis实例,实现黑名单统一管理
  3. 动态配置:可手工或通过自动化方式设置Redis中的黑名单
  4. 自动失效:封禁时间到期自动解除,无需人工干预

应用场景

  • 防止恶意访问(暴 力 破 解、SQL注入等)
  • 防止爬虫和数据滥用
  • 缓解DDoS攻击
  • 限制访问频率

优化建议

  1. 连接池调优:根据并发量调整pool_size和pool_max_idle_time
  2. Redis高可用:使用Redis哨兵或集群模式
  3. 监控告警:对封禁情况进行统计分析,及时调整策略

版权声明 : 本文内容来源于互联网或用户自行发布贡献,该文观点仅代表原作者本人。本站仅提供信息存储空间服务和不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权, 违法违规的内容, 请发送邮件至2530232025#qq.cn(#换@)举报,一经查实,本站将立刻删除。
原文链接 :
相关文章
  • 本站所有内容来源于互联网或用户自行发布,本站仅提供信息存储空间服务,不拥有版权,不承担法律责任。如有侵犯您的权益,请您联系站长处理!
  • Copyright © 2017-2022 F11.CN All Rights Reserved. F11站长开发者网 版权所有 | 苏ICP备2022031554号-1 | 51LA统计