鄂尔多斯市网站建设_网站建设公司_VS Code_seo优化
2025/12/29 13:39:54 网站建设 项目流程

一、概述

在高并发场景下,API接口防护是保证系统稳定性的关键环节。传统的Nginx限制模块(如ngx_http_limit_conn_modulengx_http_limit_req_module)虽然功能强大,但灵活性有限。本文将介绍如何结合Nginx、Lua和Redis实现更精细化的请求限制方案。

核心需求

  1. /myapi/**路径的请求进行验证
  2. 只允许POST方法访问
  3. 基于黑名单机制过滤非法请求
  4. 阻止不符合规则的请求到达后端服务器

二、技术架构

组件选型

  • Nginx: 高性能Web服务器
  • OpenResty: 集成了Nginx和LuaJIT的Web平台
  • Redis: 内存数据库,存储黑名单数据
  • Lua: 轻量级脚本语言,实现业务逻辑

架构优势

  1. 高性能: Nginx的事件驱动模型保证高并发处理能力
  2. 灵活性: Lua脚本实现业务逻辑,无需修改Nginx源码
  3. 实时性: Redis内存操作,黑名单即时生效
  4. 可扩展: 可轻松添加更多限制规则

三、环境部署

OpenResty安装

# 安装依赖yuminstall-y gcc gcc-c++ readline-devel pcre-devel openssl-devel tcl perl# 下载OpenRestywgethttps://openresty.org/download/openresty-1.19.9.1.tar.gztarxzvf openresty-1.19.9.1.tar.gzcdopenresty-1.19.9.1/# 配置编译./configure --with-http_stub_status_module\--with-http_realip_module\--with-http_v2_module\--with-stream\--with-stream_realip_module\gmake gmakeinstall# 设置环境变量echo'export PATH=/usr/local/openresty/bin:$PATH'>>~/.bashrcsource~/.bashrc

Redis安装配置

# 安装Redisyuminstall-y redis# 配置Redisvim/etc/redis.conf# 修改以下配置:# bind 127.0.0.1# requirepass yourpassword # 可选,设置密码# maxmemory 256mb# maxmemory-policy allkeys-lru# 启动Redissystemctl start redis systemctlenableredis

四、实现方案

1. Lua限制脚本

创建/usr/local/lua_test/my_access_limit.lua

-- 请求限制核心逻辑localredis=require"resty.redis"localred=redis:new()-- 设置Redis连接参数red:set_timeouts(1000,1000,1000)-- 连接/发送/接收超时(毫秒)-- 获取客户端真实IPlocalfunctionget_client_ip()localheaders=ngx.req.get_headers()localip=headers["X-Real-IP"]orheaders["x_forwarded_for"]orngx.var.remote_addr-- 处理多层代理的情况ifipandstring.find(ip,",")thenlocalips={}forvinstring.gmatch(ip,"[^,%s]+")dotable.insert(ips,v)endip=ips[1]-- 取第一个IPendreturnipend-- 主处理函数localfunctionprocess_request()-- 只处理/myapi路径下的请求ifnotngx.re.match(ngx.var.uri,"^/myapi/")thenreturnend-- 验证请求方法localmethod=ngx.var.request_methodifmethod~="POST"thenngx.log(ngx.WARN,"非POST请求被拒绝: ",ngx.var.uri)returnngx.exit(ngx.HTTP_FORBIDDEN)end-- 读取请求体ngx.req.read_body()localargs=ngx.req.get_post_args()-- 连接Redislocalok,err=red:connect("127.0.0.1",6379)ifnotokthenngx.log(ngx.ERR,"连接Redis失败: ",err)return-- 连接失败时放行请求,避免单点故障end-- 可选:Redis认证-- red:auth("yourpassword")-- 获取客户端IPlocalclient_ip=get_client_ip()-- 检查黑名单localis_blacklisted=falselocalreasons={}-- 检查IP黑名单localres,err=red:sismember("black:ip",client_ip)ifres==1thenis_blacklisted=truetable.insert(reasons,"IP在黑名单中")end-- 检查IMSI黑名单ifargs.imsithenres,err=red:sismember("black:imsi",args.imsi)ifres==1thenis_blacklisted=truetable.insert(reasons,"IMSI在黑名单中")endend-- 检查电话号码黑名单ifargs.telthenres,err=red:sismember("black:tel",args.tel)ifres==1thenis_blacklisted=truetable.insert(reasons,"电话在黑名单中")endend-- 检查设备ID黑名单ifargs.device_idthenres,err=red:sismember("black:device",args.device_id)ifres==1thenis_blacklisted=truetable.insert(reasons,"设备ID在黑名单中")endend-- 将连接放回连接池localok,err=red:set_keepalive(10000,100)ifnotokthenngx.log(ngx.ERR,"设置Redis连接池失败: ",err)end-- 处理黑名单请求ifis_blacklistedthenngx.log(ngx.WARN,"黑名单请求被阻止: ","IP=",client_ip,", URI=",ngx.var.uri,", 原因: ",table.concat(reasons,", "))-- 可选:记录详细日志ngx.var.blacklist_reason=table.concat(reasons,", ")-- 返回403禁止访问returnngx.exit(ngx.HTTP_FORBIDDEN)endend-- 执行主处理函数localok,err=pcall(process_request)ifnotokthenngx.log(ngx.ERR,"Lua脚本执行错误: ",err)-- 发生错误时放行请求,避免阻断正常流量end

2. Nginx配置

创建/usr/local/openresty/nginx/conf/nginx.conf

worker_processes auto; error_log logs/error.log info; events { worker_connections 10240; use epoll; } http { include mime.types; default_type application/octet-stream; # 日志格式添加黑名单原因字段 log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for" ' '"$blacklist_reason"'; access_log logs/access.log main; # 设置Lua包路径 lua_package_path "/usr/local/openresty/lualib/?.lua;;"; lua_package_cpath "/usr/local/openresty/lualib/?.so;;"; # 共享内存,可用于更高级的限流 lua_shared_dict my_limit_req_store 10m; lua_shared_dict my_limit_conn_store 10m; # 初始化阶段执行 init_by_lua_block { require "resty.core" } # 初始化worker时执行 init_worker_by_lua_block { -- 可在此处进行定时任务,如清理过期黑名单 } upstream backend { server 127.0.0.1:8080; keepalive 100; } server { listen 80; server_name www.mysite.com; # 响应头添加安全相关信息 add_header X-Frame-Options SAMEORIGIN; add_header X-Content-Type-Options nosniff; add_header X-XSS-Protection "1; mode=block"; # 全局黑名单检查 access_by_lua_file /usr/local/lua_test/my_access_limit.lua; # 默认路由 location / { root html; index index.html index.htm; } # API接口路由 location /myapi/ { # 请求体大小限制 client_max_body_size 1m; client_body_buffer_size 128k; # 连接后端服务器 proxy_pass http://backend; proxy_http_version 1.1; proxy_set_header Connection ""; 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_set_header X-Forwarded-Proto $scheme; # 超时设置 proxy_connect_timeout 3s; proxy_send_timeout 10s; proxy_read_timeout 10s; # 缓冲设置 proxy_buffering on; proxy_buffer_size 4k; proxy_buffers 8 4k; } # 管理接口:黑名单管理 location /admin/blacklist/ { # 限制内网访问 allow 192.168.1.0/24; allow 10.0.0.0/8; deny all; # 内容处理 default_type application/json; content_by_lua_file /usr/local/lua_test/blacklist_admin.lua; } # 状态监控接口 location /nginx_status { stub_status on; access_log off; allow 127.0.0.1; deny all; } } }

3. 黑名单管理接口

创建/usr/local/lua_test/blacklist_admin.lua

-- 黑名单管理接口localcjson=require"cjson"localredis=require"resty.redis"-- 初始化响应ngx.header.content_type="application/json; charset=utf-8"-- 连接Redislocalred=redis:new()red:set_timeouts(1000,1000,1000)localok,err=red:connect("127.0.0.1",6379)ifnotokthenngx.status=ngx.HTTP_INTERNAL_SERVER_ERROR ngx.say(cjson.encode({code=500,message="Redis连接失败: "..(error"未知错误")}))returnend-- 认证-- red:auth("yourpassword")-- 根据请求方法处理localmethod=ngx.req.get_method()ifmethod=="GET"then-- 查询黑名单localtype=ngx.var.arg_typeor"ip"localkey="black:"..typelocalitems,err=red:smembers(key)ifitemsthenngx.say(cjson.encode({code=0,message="success",data={type=type,count=#items,items=items}}))elsengx.say(cjson.encode({code=500,message="查询失败: "..(error"未知错误")}))endelseifmethod=="POST"then-- 添加黑名单ngx.req.read_body()localargs=ngx.req.get_post_args()localtype=args.typeor"ip"localvalue=args.valueifnotvaluethenngx.status=ngx.HTTP_BAD_REQUEST ngx.say(cjson.encode({code=400,message="参数value不能为空"}))returnendlocalkey="black:"..typelocaladded,err=red:sadd(key,value)ifaddedthenngx.say(cjson.encode({code=0,message="添加成功",data={type=type,value=value}}))elsengx.say(cjson.encode({code=500,message="添加失败: "..(error"未知错误")}))endelseifmethod=="DELETE"then-- 移除黑名单localtype=ngx.var.arg_typeor"ip"localvalue=ngx.var.arg_valueifnotvaluethenngx.status=ngx.HTTP_BAD_REQUEST ngx.say(cjson.encode({code=400,message="参数value不能为空"}))returnendlocalkey="black:"..typelocalremoved,err=red:srem(key,value)ifremovedthenngx.say(cjson.encode({code=0,message="移除成功",data={type=type,value=value}}))elsengx.say(cjson.encode({code=500,message="移除失败: "..(error"未知错误")}))endelsengx.status=ngx.HTTP_METHOD_NOT_ALLOWED ngx.say(cjson.encode({code=405,message="不支持的请求方法"}))end-- 关闭连接localok,err=red:set_keepalive(10000,100)ifnotokthenngx.log(ngx.ERR,"关闭Redis连接失败: ",err)end

五、使用与管理

1. 初始化黑名单

# 添加IP到黑名单redis-cli sadd black:ip"153.34.118.50"redis-cli sadd black:ip"192.168.1.100"# 添加IMSI到黑名单redis-cli sadd black:imsi"460123456789"redis-cli sadd black:imsi"460987654321"# 添加电话到黑名单redis-cli sadd black:tel"15888888888"redis-cli sadd black:tel"13966666666"# 查看黑名单redis-cli smembers black:ip redis-cli smembers black:imsi redis-cli smembers black:tel

2. 管理接口使用

# 查询IP黑名单curl"http://localhost/admin/blacklist/?type=ip"# 添加黑名单curl-X POST -d"type=ip&value=10.0.0.1""http://localhost/admin/blacklist/"# 移除黑名单curl-X DELETE"http://localhost/admin/blacklist/?type=ip&value=10.0.0.1"

3. 测试验证

# 正常POST请求curl-X POST -d"imsi=460000000001&tel=13900000000"\"http://www.mysite.com/myapi/user/login"# 黑名单IP请求(被拒绝)curl-X POST -d"imsi=460000000002&tel=13900000001"\"http://www.mysite.com/myapi/user/login"\-H"X-Real-IP: 153.34.118.50"# GET请求(被拒绝)curl"http://www.mysite.com/myapi/user/info"

六、高级功能扩展

1. 频率限制增强版

-- 基于IP的请求频率限制locallimit_req=require"resty.limit.req"locallimiter,err=limit_req.new("my_limit_req_store",10,1)-- 10 req/slocalkey=ngx.var.binary_remote_addrlocaldelay,err=limiter:incoming(key,true)ifnotdelaytheniferr=="rejected"thenngx.exit(ngx.HTTP_SERVICE_UNAVAILABLE)endngx.log(ngx.ERR,"限流失败: ",err)returnngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)endifdelay>0thenngx.sleep(delay)end

2. 动态黑名单(自动封禁)

-- 自动检测并封禁恶意IPlocalfunctionauto_ban_ip(ip,reason)localred=redis:new()localok,err=red:connect("127.0.0.1",6379)ifokthen-- 封禁IPred:sadd("black:ip:auto",ip)-- 记录封禁原因和时间red:hset("black:ip:detail",ip,ngx.encode_args({reason=reason,time=ngx.localtime(),count=1}))-- 设置过期时间(24小时自动解封)red:expire("black:ip:auto",86400)red:set_keepalive(10000,100)endend

3. 白名单机制

-- 在白名单中的请求直接放行localfunctionis_whitelist(ip)localred=redis:new()localok,err=red:connect("127.0.0.1",6379)ifokthenlocalis_white=red:sismember("white:ip",ip)red:set_keepalive(10000,100)returnis_white==1endreturnfalseend

七、性能优化建议

1. Redis优化

# 在Nginx中配置Redis连接池 lua_shared_dict redis_cluster_slot_locks 100k; lua_socket_connect_timeout 100ms; lua_socket_send_timeout 200ms; lua_socket_read_timeout 200ms;

2. 缓存优化

-- 使用共享字典缓存黑名单localfunctionis_ip_blacklisted(ip)localcache=ngx.shared.blacklist_cachelocalcached=cache:get("ip:"..ip)ifcachedthenreturncached=="1"end-- 缓存未命中,查询Redislocalred=redis:new()localok,err=red:connect("127.0.0.1",6379)ifokthenlocalis_black=red:sismember("black:ip",ip)red:set_keepalive(10000,100)-- 缓存结果,有效期5秒cache:set("ip:"..ip,is_black==1and"1"or"0",5)returnis_black==1endreturnfalseend

3. 异步处理

-- 使用ngx.timer.at进行异步处理localfunctionasync_log_blacklist(ip,reason)localok,err=ngx.timer.at(0,function()-- 异步记录日志到文件或数据库locallog_msg=string.format("%s - %s - %s\n",ngx.localtime(),ip,reason)localfile,err=io.open("/var/log/nginx/blacklist.log","a")iffilethenfile:write(log_msg)file:close()endend)ifnotokthenngx.log(ngx.ERR,"创建定时器失败: ",err)endend

八、监控与告警

1. 监控指标

# 查看Nginx状态curlhttp://localhost/nginx_status# 查看黑名单统计redis-cli scard black:ip redis-cli scard black:imsi redis-cli scard black:tel# 查看拦截日志tail-f /usr/local/openresty/nginx/logs/access.log|grep'"黑名单"'

2. 告警配置

# 监控脚本示例#!/bin/bash# 检查黑名单数量BLACKLIST_COUNT=$(redis-cli scard black:ip)if[$BLACKLIST_COUNT-gt1000];thenecho"警告:黑名单IP数量超过1000个"|mail -s"黑名单告警"admin@example.comfi# 检查拦截频率INTERCEPT_COUNT=$(grep-c'"黑名单"'/var/log/nginx/access.log2>/dev/null||echo0)if[$INTERCEPT_COUNT-gt100];thenecho"警告:过去1小时拦截超过100次"|mail -s"拦截告警"admin@example.comfi

九、总结

通过Nginx + Lua + Redis的组合,我们实现了一个灵活、高效的请求限制系统。这个方案具有以下优势:

  1. 高性能:利用Nginx的高并发处理能力和Redis的内存操作
  2. 灵活性:Lua脚本可以轻松实现各种复杂的业务逻辑
  3. 实时性:黑名单变更立即生效
  4. 可扩展:可以方便地添加新的限制规则和检查条件
  5. 易维护:配置和脚本分离,便于管理和升级

在实际生产环境中,可以根据具体需求进一步扩展功能,如添加更多维度的限制规则、实现更智能的恶意请求检测、集成监控告警系统等,从而构建更加完善的安全防护体系。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询