Nginx - 整合lua 实现对POST请求的参数拦截校验(不使用Openresty)

news2025/4/7 23:50:09

文章目录

  • 概述
  • 步骤 1: 安装 Nginx 和 Lua 模块
  • 步骤 2: 创建 Lua 脚本用于参数校验
  • 步骤 3: 配置 Nginx 使用 Lua 脚本
    • 写法二: 状态码
    • 写法三 : 返回自定义JSON
    • 复杂的正则校验
  • 步骤 4: 测试和验证
  • ngx.HTTP_* 枚举值

在这里插入图片描述


概述

一个不使用 OpenResty 的 Nginx 集成 Lua 脚本的方案,用于对 POST 请求参数进行校验。

在这里插入图片描述

指令所处处理阶段使用范围解释
init_by_lua / init_by_lua_fileloading-confighttpNginx Master进程加载配置时执行;通常用于初始化全局配置/预加载Lua模块
init_worker_by_lua / init_worker_by_lua_filestarting-workerhttp每个Nginx Worker进程启动时调用的计时器,如果Master进程不允许则只会在init_by_lua之后调用;通常用于定时拉取配置/数据,或者后端服务的健康检查
set_by_lua / set_by_lua_filerewriteserver, server if, location, location if设置Nginx变量,可以实现复杂的赋值逻辑;此处是阻塞的,Lua代码要做到非常快。
rewrite_by_lua / rewrite_by_lua_filerewritehttp, server, location, location ifrewrite阶段处理,可以实现复杂的转发/重定向逻辑。
access_by_lua / access_by_lua_fileaccesshttp, server, location, location if请求访问阶段处理,用于访问控制。
content_by_lua / content_by_lua_filecontentlocation, location if内容处理器,接收请求处理并输出响应。
header_filter_by_lua / header_filter_by_lua_fileoutput-header-filterhttp, server, location, location if设置header和cookie。
body_filter_by_lua / body_filter_by_lua_fileoutput-body-filterhttp, server, location, location if对响应数据进行过滤,比如截断、替换。
log_by_lua / log_by_lua_fileloghttp, server, location, location iflog阶段处理,比如记录访问量/统计平均响应时间。

步骤 1: 安装 Nginx 和 Lua 模块

玩转 Nginx 之:使用 Lua 扩展 Nginx 功能

在这里插入图片描述


步骤 2: 创建 Lua 脚本用于参数校验

创建一个 Lua 脚本,位于 /opt/nginx/lib/lua/validate_params.lua(路径根据nginx.conf中的位置调整):

 -- validate_params.lua
local function validate_post_params()
    ngx.req.read_body()
    local args = ngx.req.get_post_args()

    if not args.username or #args.username < 3 then
        ngx.status = ngx.HTTP_BAD_REQUEST
        ngx.say("Username must be at least 3 characters long")
        return false
    end

    if not args.password or #args.password < 6 then
        ngx.status = ngx.HTTP_BAD_REQUEST
        ngx.say("Password must be at least 6 characters long")
        return false
    end

    return true
end

return {
    validate_post_params = validate_post_params
}


步骤 3: 配置 Nginx 使用 Lua 脚本

在 Nginx 配置文件中,使用 Lua 脚本来校验 POST 请求参数。修改 nginx.conf` ,增加 Lua 配置:

 
user  root;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;
    
    lua_package_path "/opt/nginx/lib/lua/?.lua;;";

    lua_need_request_body on;  # 启用请求体读取
    
    

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  logs/access.log  main;
    error_log  logs/error.log ;
    
    
    sendfile        on;

    keepalive_timeout  65;

    
    server {
        listen       28443;
        server_name  localhost;

        access_log  logs/host.access.log  main;

        location / {
            root   html;
            index  index.html index.htm;
        }
 
        
   location /api {
    content_by_lua_block {
        local validator = require("validate_params")
        if validator.validate_post_params() then
            ngx.say("Validation passed")
        end
        }
      }
   }
}


写法二: 状态码

lua

-- validate_params.lua
local function validate_post_params()
    ngx.req.read_body()
    local args = ngx.req.get_post_args()

    -- 验证用户名
    if not args.username or #args.username < 3 then
        return ngx.HTTP_BAD_REQUEST, "Invalid username"
    end

    -- 验证密码
    if not args.password or #args.password < 6 then
        return ngx.HTTP_BAD_REQUEST, "Invalid password"
    end

    -- 验证通过
    return ngx.HTTP_OK, "Validation successful"
end

return {
    validate = validate_post_params
}

nginx.conf location config

  location /api {
    content_by_lua_block {
        local validator = require("validate_params")
        local status, message = validator.validate()
        
        ngx.status = status
        ngx.say(status)
        ngx.say(message)
        
        if status ~= ngx.HTTP_OK then
            ngx.exit(status)
        end
        
        -- 继续后续业务逻辑
    }
  }

在这里插入图片描述


写法三 : 返回自定义JSON

如果不使用 cjson 库,我们可以手动构造 JSON 字符串。这种方法虽然不如使用专门的 JSON 库灵活,但对于简单的情况来说是足够的。

 -- validate_params.lua
local function validate_post_params()
    ngx.req.read_body()
    local args = ngx.req.get_post_args()

    if not args.username or #args.username < 3 then
        ngx.status = ngx.HTTP_BAD_REQUEST
        ngx.say("Username must be at least 3 characters long")
        return false
    end

    if not args.password or #args.password < 6 then
        ngx.status = ngx.HTTP_BAD_REQUEST
        -- 创建 JSON 字符串
        local json_response = string.format('{"status":"%s","message":"%s"}',ngx.status,  "Password must be at least 6 characters long")

        -- 设置响应头为 JSON
        ngx.header.content_type = "application/json"
        
        -- 输出 JSON 响应
        ngx.say(json_response)

        return false
    end

    return true
end

return {
    validate_post_params = validate_post_params
}

这段代码会返回相同格式的 JSON:

{
    "status": "400",
    "message": "Password must be at least 6 characters long"
}

在这里插入图片描述

几点说明:

  1. 使用 Lua 的 string.format() 函数来构造 JSON 字符串。这种方法适用于简单的 JSON 结构。

  2. 注意要正确处理字符串中的特殊字符,特别是引号。在这个例子中,我们的消息没有特殊字符,但在实际应用中可能需要进行转义。

  3. 仍然设置响应头的 content-type 为 “application/json”。

  4. 使用 ngx.say() 输出构造的 JSON 字符串。

这种方法的优点是不依赖额外的库,缺点是对于复杂的 JSON 结构可能会变得难以维护。如果需要处理更复杂的 JSON 数据,或者需要频繁地进行 JSON 操作,最好还是使用专门的 JSON 库(如 cjson 或 dkjson)。


 location /api {
    content_by_lua_block {
        local validator = require("validate_params")
        if validator.validate_post_params() then
            ngx.say("Validation passed")
        end
        }
      }

复杂的正则校验

-- validate_params.lua

-- 用户名验证函数
local function validate_username(username)
    -- 基础检查
    if not username or username == "" then
        return false, "Username cannot be empty"
    end

    -- 长度检查
    local length = string.len(username)
    if length < 3 then
        return false, "Username is too short (minimum 3 characters)"
    end
    if length > 20 then
        return false, "Username is too long (maximum 20 characters)"
    end

    -- 检查是否包含空格
    if string.find(username, "%s") then
        return false, "Username cannot contain spaces"
    end

    -- 正则表达式检查(只允许字母、数字、下划线,必须以字母开头)
    local pattern = "^[A-Za-z][A-Za-z0-9_]*$"
    if not ngx.re.match(username, pattern) then
        return false, "Username must start with a letter and can only contain letters, numbers and underscore"
    end

    -- 检查保留字
    local reserved_words = {
        "admin", "root", "system", "user",
        "moderator", "administrator"
    }
    
    local username_lower = string.lower(username)
    for _, word in ipairs(reserved_words) do
        if username_lower == word then
            return false, "This username is reserved"
        end
    end

    return true, nil
end

-- JSON响应函数
local function send_json_response(status, message)
    ngx.status = status
    ngx.header.content_type = "application/json"
    local json_response = string.format('{"status":"%s","message":"%s"}', status, message)
    ngx.say(json_response)
end

-- 主验证函数
local function validate_post_params()
    ngx.req.read_body()
    local args = ngx.req.get_post_args()

    -- 验证用户名
    if not args.username then
        send_json_response(ngx.HTTP_BAD_REQUEST, "Username is required")
        return false
    end

    local is_valid, error_message = validate_username(args.username)
    if not is_valid then
        send_json_response(ngx.HTTP_BAD_REQUEST, error_message)
        return false
    end

    -- 验证密码
    if not args.password or #args.password < 6 then
        send_json_response(ngx.HTTP_BAD_REQUEST, "Password must be at least 6 characters long")
        return false
    end

    return true
end

-- 导出模块
return {
    validate_post_params = validate_post_params
}


    location /api {
    content_by_lua_block {
        local validator = require("validate_params")
        if validator.validate_post_params() then
            ngx.say("Validation passed")
        end
        }
      }

在这里插入图片描述

  1. 有效的用户名和密码:
curl -X POST http://localhost:28443/api -d "username=validuser&password=validpass123"

预期结果:成功(具体响应取决于你的成功处理逻辑)

  1. 用户名太短:
curl -X POST http://localhost:28443/api -d "username=ab&password=validpass123"

预期结果:

{"status":"400","message":"Username is too short (minimum 3 characters)"}
  1. 用户名太长:
curl -X POST http://localhost:28443/api -d "username=thisusernameiswaytoolong&password=validpass123"

预期结果:

{"status":"400","message":"Username is too long (maximum 20 characters)"}
  1. 用户名包含空格:
curl -X POST http://localhost:28443/api -d "username=invalid user&password=validpass123"

预期结果:

{"status":"400","message":"Username cannot contain spaces"}
  1. 用户名不以字母开头:
curl -X POST http://localhost:28443/api -d "username=1invaliduser&password=validpass123"

预期结果:

{"status":"400","message":"Username must start with a letter and can only contain letters, numbers and underscore"}
  1. 用户名包含非法字符:
curl -X POST http://localhost:28443/api -d "username=invalid@user&password=validpass123"

预期结果:

{"status":"400","message":"Username must start with a letter and can only contain letters, numbers and underscore"}
  1. 用户名是保留字:
curl -X POST http://localhost:28443/api -d "username=admin&password=validpass123"

预期结果:

{"status":"400","message":"This username is reserved"}
  1. 密码太短:
curl -X POST http://localhost:28443/api -d "username=validuser&password=short"

预期结果:

{"status":"400","message":"Password must be at least 6 characters long"}
  1. 缺少用户名:
curl -X POST http://localhost:28443/api -d "password=validpass123"

预期结果:

{"status":"400","message":"Username is required"}
  1. 缺少密码:
curl -X POST http://localhost:28443/api -d "username=validuser"

预期结果:

{"status":"400","message":"Password must be at least 6 characters long"}

步骤 4: 测试和验证

  1. 重启 Nginx

    sudo systemctl restart nginx
    
  2. 发送 POST 请求
    可以使用 curl 或 Postman 测试:

[root@hcss-ecs-917b sbin]#    curl -X POST http://localhost:28443/api -d "username=test&password=123456"
Validation passed
[root@hcss-ecs-917b sbin]#
[root@hcss-ecs-917b sbin]#
[root@hcss-ecs-917b sbin]#
[root@hcss-ecs-917b sbin]#    curl -X POST http://localhost:28443/api -d "username=test&password=1"
Password must be at least 6 characters long
[root@hcss-ecs-917b sbin]#
[root@hcss-ecs-917b sbin]#
[root@hcss-ecs-917b sbin]#
[root@hcss-ecs-917b sbin]#    curl -X POST http://localhost:28443/api -d "username=t&password=1"
Username must be at least 3 characters long
[root@hcss-ecs-917b sbin]#
[root@hcss-ecs-917b sbin]#
[root@hcss-ecs-917b sbin]#
[root@hcss-ecs-917b sbin]#
[root@hcss-ecs-917b sbin]#
[root@hcss-ecs-917b sbin]#    curl -X POST http://localhost:28443/api
Username must be at least 3 characters long
[root@hcss-ecs-917b sbin]#
[root@hcss-ecs-917b sbin]#

在这里插入图片描述


ngx.HTTP_* 枚举值

在 OpenResty/Nginx 中,ngx.HTTP_* 常用的状态码枚举值如下:

  1. 成功类状态码:
ngx.HTTP_OK                 -- 200 成功
ngx.HTTP_CREATED            -- 201 已创建
ngx.HTTP_NO_CONTENT         -- 204 无内容
  1. 重定向类:
ngx.HTTP_MOVED_TEMPORARILY  -- 302 临时重定向
ngx.HTTP_MOVED_PERMANENTLY  -- 301 永久重定向
ngx.HTTP_SEE_OTHER          -- 303 其他位置
ngx.HTTP_NOT_MODIFIED       -- 304 未修改
  1. 客户端错误类:
ngx.HTTP_BAD_REQUEST        -- 400 错误请求
ngx.HTTP_UNAUTHORIZED       -- 401 未授权
ngx.HTTP_FORBIDDEN          -- 403 禁止访问
ngx.HTTP_NOT_FOUND          -- 404 未找到
ngx.HTTP_METHOD_NOT_ALLOWED -- 405 方法不允许
ngx.HTTP_REQUEST_TIMEOUT    -- 408 请求超时
ngx.HTTP_CONFLICT           -- 409 冲突
ngx.HTTP_GONE               -- 410 资源已不存在
  1. 服务器错误类:
ngx.HTTP_INTERNAL_SERVER_ERROR  -- 500 内部服务器错误
ngx.HTTP_NOT_IMPLEMENTED        -- 501 未实现
ngx.HTTP_BAD_GATEWAY            -- 502 网关错误
ngx.HTTP_SERVICE_UNAVAILABLE    -- 503 服务不可用
ngx.HTTP_GATEWAY_TIMEOUT        -- 504 网关超时

常用示例:

-- 成功响应
ngx.status = ngx.HTTP_OK
ngx.say("Success")

-- 参数错误
ngx.status = ngx.HTTP_BAD_REQUEST
ngx.say("Invalid parameters")

-- 未授权
ngx.status = ngx.HTTP_UNAUTHORIZED
ngx.say("Authentication required")

-- 服务器错误
ngx.status = ngx.HTTP_INTERNAL_SERVER_ERROR
ngx.say("Server error occurred")

推荐使用场景:

  1. 验证类:使用 400-409
  2. 身份认证:使用 401-403
  3. 资源相关:使用 404-410
  4. 服务器错误:使用 500-504

建议根据具体业务场景选择最精准的状态码。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2268651.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

在 React 项目中安装和配置 Three.js

React 与 Three.js 的结合 &#xff1a;通过 React 管理组件化结构和应用逻辑&#xff0c;利用 Three.js 实现 3D 图形的渲染与交互。使用这种方法&#xff0c;我们可以在保持代码清晰和结构化的同时&#xff0c;实现令人惊叹的 3D 效果。 在本文中&#xff0c;我们将以一个简…

logstash 对配置文件conf敏感信息加密处理

logstash的配置文件conf经常会涉及敏感信息&#xff0c;比如ES&#xff0c;数据库的账户密码等&#xff0c;以下使用logstash导入ORACLE为例子&#xff0c;加密隐藏ORACLE的密码。 1.先创建keystore&#xff0c;可以不设置keystore密码,直接选择y 在logstash目录下&#xff0…

为什么MoE推理效率更高:精简FFN

MoE全称是“混合专家”,它由多个专家网络和一个门控网络组成……整个MoE完全复用了Transformer的结构,只是将其中的FFN层替换成了MoE层。MoE层里的门控网络其实就是个专家分类器,每次根据输入Token生成专家的概率分布,然后选择排序靠前的K个专家进行Token处理,最后再将K个…

课设CLion连接Ubuntu14makeQt项目出错解决汇总

在这之前需要注意以下几点&#xff1a; 1、需要 确保CLion能连接Ubuntu14 2、cmakelist.txt文件配置 3、知道部署路径&#xff1a; 问题一&#xff1a;/usr/bin/ld: cannot open output file GreedySnake: Is a directory 否则就会出现make以后应该生成一个可执行文件&…

第T4周:TensorFlow实现猴痘识别(Tensorboard的使用)

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 目标&#xff1a; 1、学习tensorboard的使用 具体实现&#xff1a; &#xff08;一&#xff09;环境&#xff1a; 语言环境&#xff1a;Python 3.10 编 译 器…

LeetCode:513.找二叉树左下角的

跟着carl学算法&#xff0c;本系列博客仅做个人记录&#xff0c;建议大家都去看carl本人的博客&#xff0c;写的真的很好的&#xff01; 代码随想录 LeetCode&#xff1a;513.找二叉树左下角的 给定一个二叉树的 根节点 root&#xff0c;请找出该二叉树的 最底层 最左边 节点的…

【Redis】Redis 典型应用 - 缓存 (cache)

目录 1. 什么是缓存 2. 使用 Redis 作为缓存 3. 缓存的更新策略 3.1 定期生成 3.2 实时生成 4. 缓存的淘汰策略 5. 缓存预热, 缓存穿透, 缓存雪崩 和 缓存击穿 关于缓存预热 (Cache preheating) 关于缓存穿透 (Cache penetration) 关于缓存雪崩 (Cache avalanche) 关…

【Rust自学】7.6. 将模块拆分为不同文件

喜欢的话别忘了点赞、收藏加关注哦&#xff08;加关注即可阅读全文&#xff09;&#xff0c;对接下来的教程有兴趣的可以关注专栏。谢谢喵&#xff01;(&#xff65;ω&#xff65;) 7.6.1. 将模块的内容移动到其他文件 如果在模块定义时模块名后边跟的是;而不是代码块&#…

随机种子定不死找bug

记录一次debug的心路历程 在运行别人的开源项目时遇到了随机种子定不死的情况, 运行一开始会有1e-5次方左右的误差, 后面误差会越来越大。 一开始以为是随机种子没有定死, 使用的以下代码固定的随机种子: torch.manual_seed(seed)torch.cuda.manual_seed(seed)torch.cuda.manu…

STM32学习之 按键/光敏电阻 控制 LED/蜂鸣器

STM32学习之 按键/光敏电阻 控制 LED/蜂鸣器 1、按键控制 LED 按键:常见的输入设备&#xff0c;按下导通&#xff0c;松手断开 按键抖动:由子按键内部使用的是机械式弹簧片来进行通断的、所以在按下和松手的瞬间会伴随有一连串的抖动 按键控制LED接线图&#xff1a; 要有工程…

深入理解Redis:从理论到实践的Java之旅

Redis&#xff0c;这个开源的内存数据结构存储系统&#xff0c;自2009年诞生以来&#xff0c;凭借其丰富的数据结构、快速的读写性能以及高度的可扩展性&#xff0c;迅速成为了分布式系统和高并发应用中的明星组件。本文将带你深入理解Redis&#xff0c;并通过Java语言的实践示…

VS Code中怎样查看某分支的提交历史记录

VsCode中无法直接查看某分支的提交记录&#xff0c;需借助插件才行&#xff0c;常见的插件如果git history只能查看某页面的改动记录&#xff0c;无法查看某分支的整体提交记录&#xff0c;我们可以安装GIT Graph插件来解决这个问题 1.在 VSCode的插件库中搜索 GIT Graph安装&a…

Flutter适配HarmonyOS实践

大家在知道纯血鸿蒙到来的时候一定很疑惑&#xff0c;使用跨平台语言的到底该怎么办&#xff0c;不管使用Flutter还是使用原生鸿蒙开发&#xff0c;都会考虑到一个成本问题&#xff0c;特别是一些无法支持鸿蒙开发团队&#xff0c;已经使用跨平台开发已经很成熟的公司或者团队。…

XIAO Esp32S3 播放网络Mp3

本文旨在使用XIAO Esp32S3 播放网络Mp3 所需硬件 max98357 接线 Xiao Esp32 S3Max983574LRC5BCLK 6DIN5VVinGNDGND代码: #include "Arduino.h" #include "WiFiMulti.h" #include "Audio.h"// Digital I/O used #def

智能边缘计算×软硬件一体化:开启全场景效能革命新征程(高校开发者作品)

边缘智能技术快速迭代&#xff0c;并与行业深度融合。它正重塑产业格局&#xff0c;催生新产品、新体验&#xff0c;带动终端需求增长。为促进边缘智能技术的进步与发展&#xff0c;拓展开发者的思路与能力&#xff0c;挖掘边缘智能应用的创新与潜能&#xff0c;高通技术公司联…

ubuntu22 安装CUDA

在Ubuntu系统中&#xff0c;使用nvidia-smi命令可以看到当前GPU信息&#xff0c;在右上角可以看到CUDA Version&#xff0c;意思是最大支持的CUDA版本号。 安装下载 CUDA Toolkit 11.6 Downloads | NVIDIA Developer https://developer.nvidia.com/cuda-downloads?target_osL…

VectorCAST入门指导

文章目录 1. VectorCAST 概述2. 启动 VectorCAST(Windows用户)2. 故障排除3. VectorCAST 界面4. 创建一个 VectorCAST 项目5. 设置工作目录6. 创建一个新项目7. 添加一个单元测试环境8. 添加测试用例9. 执行所有测试1. VectorCAST 概述 VectorCAST是一套测试自动化工具: Vecto…

12.30 linux 文件操作,磁盘分区挂载

ubuntu 在linux 对文件的相关操作【压缩&#xff0c;打包&#xff0c;软链接&#xff0c;文件权限】【head&#xff0c;tail&#xff0c;管道符&#xff0c;通配符&#xff0c;find&#xff0c;grep&#xff0c;cut等】脑图-CSDN博客 1.文件操作 在家目录下创建目录文件&#…

Zabbix 监控平台 添加监控目标主机

Zabbix监控平台是一个企业级开源解决方案&#xff0c;用于分布式系统监视和网络监视。它由Zabbix Server和可选组件Zabbix Agent组成&#xff0c;通过C/S模式&#xff08;客户端-服务器模型&#xff09;采集数据&#xff0c;并通过B/S模式&#xff08;浏览器-服务器模型&#x…

第10章 初等数论

2024年12月27日一稿&#xff08;P341&#xff09; 2024年12月28日二稿 2024年12月29日三稿 当命运这扇大门向你打开的时候&#xff0c;不要犹豫和害怕&#xff0c;一直往前跑就是了&#xff01; 10.1 素数 这里写错了&#xff0c;不能整除应该表示为 10.2 最大公约数与最小公…