如何在 Nginx 层通过 lua 脚本实现简单的 API Key 鉴权?

文章导读
在 Nginx 层通过 Lua 脚本实现 API Key 鉴权,适合需要在网关层统一拦截请求、且不想修改后端业务代码的场景,通常建议使用集成 Lua 环境的 OpenResty 发行版。
📋 目录
  1. A 核心配置示例
  2. B 生产环境密钥管理
  3. C 验证步骤
  4. D 常见坑与排查
A A

在 Nginx 层通过 Lua 脚本实现 API Key 鉴权,适合需要在网关层统一拦截请求、且不想修改后端业务代码的场景,通常建议使用集成 Lua 环境的 OpenResty 发行版。

先说结论:利用 Nginx 的 lua-nginx-module 模块可以在请求到达后端前完成密钥校验,实现轻量级 access 控制。

  • 先判断:确认当前 Nginx 是否编译了 Lua 支持,普通 Nginx 默认不支持,需使用 OpenResty 或手动编译模块。
  • 优先做:在 location 或 server 块中使用 access_by_lua_block 编写校验逻辑,匹配 Header 中的 Key(注意键名小写)。
  • 再验证:通过 curl 命令携带或不携带 Key 测试接口返回状态码,确认拦截生效。

核心配置示例

核心是在 Nginx 配置文件中嵌入 Lua 代码片段,检查请求头是否包含合法的 API Key。以下是一个最小可用的配置片段,可直接参考放入 server 块中:

location /api/ {    access_by_lua_block {        local api_key = ngx.req.get_headers()["x-api-key"]        if not api_key or api_key ~= "your_secret_key" then            ngx.status = 401            ngx.header["Content-Type"] = "application/json"            ngx.say('{"error": "Unauthorized"}')            return ngx.exit(401)        end    }    proxy_pass http://backend_server;}

这段配置会在请求进入 /api/ 路径时优先执行 Lua 脚本,校验通过才放行到后端。注意:ngx.req.get_headers() 默认返回小写键名,因此代码中必须使用 "x-api-key" 而非 "X-API-Key",否则会导致获取不到值。

生产环境密钥管理

示例中密钥硬编码在配置里仅适合测试。生产环境建议将密钥存储在共享字典或 Redis 中,避免每次重载配置。以下是基于 ngx.shared.DICT 的实现方案:

1. 定义共享字典:在 http 块中添加 lua_shared_dict 配置:

如何在 Nginx 层通过 lua 脚本实现简单的 API Key 鉴权?
http {    lua_shared_dict api_keys 10m;    ...}

2. 读取逻辑:在 access_by_lua_block 中从字典获取白名单密钥:

location /api/ {    access_by_lua_block {        local api_key = ngx.req.get_headers()["x-api-key"]        local keys_dict = ngx.shared.api_keys        local valid_key = keys_dict:get("valid_key")         if not api_key or api_key ~= valid_key then            ngx.status = 401            ngx.header["Content-Type"] = "application/json"            ngx.say('{"error": "Unauthorized"}')            return ngx.exit(401)        end    }    proxy_pass http://backend_server;}

若需对接 Redis,需引入 resty.redis 库,逻辑类似但需注意连接池管理及超时设置,避免阻塞 Nginx worker 进程。

验证步骤

使用 curl 命令模拟请求进行测试。首先测试不带 Key 的情况:

curl -i http://your-domain/api/test

预期返回 HTTP 状态码 401 且 body 包含错误信息。接着测试带正确 Key 的情况:

curl -i -H "x-api-key: your_secret_key" http://your-domain/api/test

预期返回后端服务的正常响应(如 200 状态码)。如果两次测试结果符合预期,说明鉴权逻辑已生效。

如何在 Nginx 层通过 lua 脚本实现简单的 API Key 鉴权?

常见坑与排查

1. 大小写敏感:HTTP 请求头键名在 Lua 获取时通常标准化为小写。代码中务必使用小写键名(如 "x-api-key")进行匹配,否则会导致获取不到值而拦截所有请求。

2. 性能损耗:Lua 脚本执行会带来轻微的计算开销,如果在高并发场景下频繁访问外部数据库验证 Key,可能拖慢 Nginx 响应。建议优先使用本地缓存或共享字典。

3. 密钥泄露:配置文件中明文存储密钥存在风险。确保配置文件权限设置为仅 root 可读,且务必开启 HTTPS,防止 Key 在传输过程中被窃听。

4. 错误处理:Lua 脚本执行出错可能导致 Nginx 返回 500 错误。建议在 Lua 块中使用 pcall 包裹逻辑,或确保语法简单可靠,避免复杂依赖。

5. 配置生效:修改配置后执行 nginx -t 检查语法,无误后执行 nginx -s reload 生效。注意不要直接 restart,以免中断现有连接。